From 17e0bf00b569144e6835d0ab28585d0397d3a23b Mon Sep 17 00:00:00 2001 From: Gavin Stewart Date: Mon, 2 May 2016 11:09:34 +1000 Subject: [PATCH 01/83] Update IIS web.config - add .git blocking. --- web.config.dist | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/web.config.dist b/web.config.dist index 00f8389..aeea334 100644 --- a/web.config.dist +++ b/web.config.dist @@ -8,11 +8,25 @@ - + + + + + + + + + + + + - + @@ -30,6 +45,7 @@ + From bc281f8e70f78c4d33ee02029aad1cd56b28590c Mon Sep 17 00:00:00 2001 From: Gavin Stewart Date: Tue, 24 May 2016 17:44:18 +1000 Subject: [PATCH 02/83] New RESTfmMessage interface to replace old RESTfmData - WIP. --- lib/RESTfm/RESTfmMessage.php | 18 ++ lib/RESTfm/RESTfmMessageInterface.php | 246 ++++++++++++++++++++++++++ lib/RESTfm/Version.php | 2 +- 3 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 lib/RESTfm/RESTfmMessage.php create mode 100644 lib/RESTfm/RESTfmMessageInterface.php diff --git a/lib/RESTfm/RESTfmMessage.php b/lib/RESTfm/RESTfmMessage.php new file mode 100644 index 0000000..0500c4c --- /dev/null +++ b/lib/RESTfm/RESTfmMessage.php @@ -0,0 +1,18 @@ + RESTfmMessage -> backend + * Response: backend -> RESTfmMessage -> export format + * + * In practice, responses are created from raised exceptions as well. + * + * Not every request will create a RESTfmMessage as some requests contain + * no actual data. + * + * Every response will contain data and so will create a RESTfmMessage. + */ +interface RESTfmMessageInterface { + + // --- Access methods for inserting/manipulating data --- // + + /** + * Add or update a key/value pair to 'info' section. + * + * @param string $key + * @param string $val + */ + public function addInfo ($key, $val); + + /** + * @return associative array of key/value pairs. + */ + public function getInfo (); + + /** + * Add a message row object to 'metaField' section. + * + * @param RESTfmMessageRowInterface $metaField + */ + public function addMetaField (RESTfmMessageRowInterface $metaField); + + /** + * @return array of RESTfmMessageRowInterface. + */ + public function getMetaFields (); + + /** + * Add a message multistatus object to 'multistatus' section. + * + * @param RESTfmMessageMultistatusInterface $multistatus + */ + public function addMultistatus (RESTfmMessageMultistatusInterface $multistatus); + + /** + * @return array of RESTfmMessageMultistatusInterface. + */ + public function getMultistatus (); + + /** + * Add a message row object to 'nav' section. + * + * @param RESTfmMessageRowInterface $nav + */ + public function addNav (RESTfmMessageRowInterface $nav); + + /** + * @return array of RESTfmMessageRowInterface. + */ + public function getNavs (); + + /** + * Add a message record object that contains data for 'data' and 'meta' + * sections. + * + * @param RESTfmMessageRecordInterface $record + */ + public function addRecord (RESTfmMessageRecordInterface $record); + + /** + * @return array of RESTfmMessageRecordInterface. + */ + public function getRecords (); + + + // --- Access methods for reading data as sections (export formats) --- // + + /** + * @return array of strings of available section names. + * Section names are: nav, data, meta, metaField, info, multistatus + * + */ + public function getSectionNames (); + + /** + * @param string $name section name. + * + * @return RESTfmMessageSectionInterface + */ + public function getSection ($name); + + /** + * Make a human readable string from stored contents. + * + * @return string + */ + public function __toString (); +} diff --git a/lib/RESTfm/Version.php b/lib/RESTfm/Version.php index 308d74b..dd2ba11 100644 --- a/lib/RESTfm/Version.php +++ b/lib/RESTfm/Version.php @@ -21,7 +21,7 @@ * Version static class to hold release version. */ class Version { - private static $_release = '4.0.3'; + private static $_release = 'dev'; private static $_revision = '%%REVISION%%'; private static $_protocol = '5'; // Bump this when REST API changes. From b9604321d36f7dd6748daaf936ec9c01fed1bd53 Mon Sep 17 00:00:00 2001 From: Gavin Stewart Date: Thu, 26 May 2016 17:30:00 +1000 Subject: [PATCH 03/83] RESTfmMessage implementation - first cut. --- lib/RESTfm/RESTfmMessage.php | 393 ++++++++++++++++++++++++++ lib/RESTfm/RESTfmMessageInterface.php | 25 +- 2 files changed, 413 insertions(+), 5 deletions(-) diff --git a/lib/RESTfm/RESTfmMessage.php b/lib/RESTfm/RESTfmMessage.php index 0500c4c..0bfbda6 100644 --- a/lib/RESTfm/RESTfmMessage.php +++ b/lib/RESTfm/RESTfmMessage.php @@ -16,3 +16,396 @@ * @author * Gavin Stewart */ + +require_once 'RESTfmMessageInterface.php'; + +class RESTfmMessageRow implements RESTfmMessageRowInterface { + + protected $_data = array(); + + /** + * @return associative array of key/value pairs. + */ + public function getData () { + return $data; + } + + /** + * @param array $assocArray + * Set row data with provided array of key/value pairs. + */ + public function setData ($assocArray) { + $this->_data = $assocArray; + } + + /** + * @param string $fieldName + * + * @return mixed + */ + public function getField ($fieldName) { + if (isset($this->_data[$fieldName])) { return $this->_data[$fieldName]; } + } + + /** + * @param string $fieldName + * @param mixed $fieldValue + */ + public function setField ($fieldName, $fieldValue) { + $this->_data[$fieldName] = $fieldValue; + } + + /** + * @param string $fieldName + */ + public function unsetField ($fieldName) { + if (isset($this->_data[$fieldName])) { unset($this->_data[$fieldName]); } + } + + /** + * RESTfmMessage internal function. + */ + public function &_getDataReference () { + return $this->_data; + } +} + +class RESTfmMessageRecord extends RESTfmMessageRow implements RESTfmMessageRecordInterface { + + protected $_meta = array(); + + /** + * @return string + */ + public function getHref () { + if (isset($this->_meta['href'])) return $this->_meta['href']; + } + + /** + * @param string $href + */ + public function setHref ($href) { + $this->_meta['href'] = $href; + } + + /** + * @return string + */ + public function getRecordId () { + if (isset($this->_meta['recordID'])) return $this->_meta['recordID']; + } + + /** + * @param string $recordID + */ + public function setRecordId ($recordID) { + $this->_meta['recordID'] = $recordID; + } + + /** + * RESTfmMessage internal function. + */ + public function &_getMetaReference () { + return $this->_meta; + } +} + +class RESTfmMessageMultistatus implements RESTfmMessageMultistatusInterface { + + protected $_multiStatus = array(); + + /** + * @return integer + */ + public function getIndex () { + if (isset($this->_multiStatus['index'])) return $this->_multiStatus['index']; + } + + /** + * @param integer $dataRowIndex + * Index of row in request's data section that caused the error. + */ + public function setIndex ($dataRowIndex) { + $this->_multiStatus['index'] = $dataRowIndex; + } + + /** + * @return string + */ + public function getStatus () { + if (isset($this->_multiStatus['Status'])) return $this->_multiStatus['Status']; + } + + /** + * @param integer $statusCode + */ + public function setStatus ($statusCode) { + $this->_multiStatus['Status'] = $statusCode; + } + + /** + * @return string + */ + public function getReason () { + if (isset($this->_multiStatus['Reason'])) return $this->_multiStatus['Reason']; + } + + /** + * @param string $reasonMessage + */ + public function setReason ($reasonMessage) { + $this->_multiStatus['Reason'] = $statusCode; + } + + /** + * @return string + */ + public function getRecordId () { + if (isset($this->_multiStatus['recordID'])) return $this->_multiStatus['recordID']; + } + + /** + * @param string $recordId + */ + public function setRecordId ($recordId) { + $this->_multiStatus['recordID'] = $recordId; + } + + /** + * RESTfmMessage internal function. + */ + public function &_getMultistatusReference () { + return $this->_multiStatus; + } +} + +class RESTfmMessageSection implements RESTfmMessageSectionInterface { + + protected $_dimensions = 0; + protected $_name = ""; + protected $_rows = array(); + + /** + * @return integer number of dimensions of data for this section. + */ + public function getDimensions () { + return $this->_dimensions; + } + + /** + * @return string name of this section. + */ + public function getName () { + return $this->_name; + } + + /** + * Returns an array of one or more message rows. + * Note: A section with only one dimension has only one row. + * Note: A section with two dimensions may have more than one row. + * + * @return array of RESTfmMessageRowInterface. + */ + public function getRows () { + return $this->_rows; + } + + /** + * RESTfmMessage internal function. + */ + public function _setName ($name) { + $this->_name = $name; + } + + /** + * RESTfmMessage internal function. + */ + public function _setDimensions ($dimensions) { + $this->_dimensions = $dimensions; + } +} + +class RESTfmMessage implements RESTfmMessageInterface { + + protected $_info = array(); + protected $_metaFields = array(); + protected $_multistatus = array(); + protected $_navs = array(); + protected $_records = array(); + + // --- Access methods for inserting/manipulating data --- // + + /** + * Add or update a key/value pair to 'info' section. + * + * @param string $key + * @param string $val + */ + public function addInfo ($key, $val) { + $this->_info[$key] = $val; + } + + /** + * @return associative array of key/value pairs. + */ + public function getInfo () { + return $this->_info; + } + + /** + * Add a message row object to 'metaField' section. + * + * @param RESTfmMessageRowInterface $metaField + */ + public function addMetaField (RESTfmMessageRowInterface $metaField) { + $this->_metaFields[] = $metaField; + } + + /** + * @return array of RESTfmMessageRowInterface. + */ + public function getMetaFields () { + return $this->_metaFields; + } + + /** + * Add a message multistatus object to 'multistatus' section. + * + * @param RESTfmMessageMultistatusInterface $multistatus + */ + public function addMultistatus (RESTfmMessageMultistatusInterface $multistatus) { + $this->_multistatus[] = $multistatus; + } + + /** + * @return array of RESTfmMessageMultistatusInterface. + */ + public function getMultistatus () { + return $this->_multistatus; + } + + /** + * Add a message row object to 'nav' section. + * + * @param RESTfmMessageRowInterface $nav + */ + public function addNav (RESTfmMessageRowInterface $nav) { + $this->_navs[] = $nav; + } + + /** + * @return array of RESTfmMessageRowInterface. + */ + public function getNavs () { + return $this->_navs; + } + + /** + * Add a message record object that contains data for 'data' and 'meta' + * sections. + * + * @param RESTfmMessageRecordInterface $record + */ + public function addRecord (RESTfmMessageRecordInterface $record) { + $this->_records[] = $record; + } + + /** + * @return array of RESTfmMessageRecordInterface. + */ + public function getRecords () { + return $this->_records; + } + + + // --- Access methods for reading data as sections (export formats) --- // + + /** + * @return array of strings of available section names. + * Section names are: nav, data, meta, metaField, info, multistatus + * + */ + public function getSectionNames () { + $availableSections = array(); + + // Sort as 'meta', 'data', 'info', . + if (!empty($this->_records)) { + $availableSections['meta'] = TRUE; + $availableSections['data'] = TRUE; + } + if (!empty($this->_info)) { $availableSections['info'] = TRUE; } + if (!empty($this->_metaFields)) { $availableSections['metaField'] = TRUE; } + if (!empty($this->_multistatus)) { $availableSections['multistatus'] = TRUE; } + if (!empty($this->_navs)) { $availableSections['nav'] = TRUE; } + + return array_keys($availableSections); + } + + /** + * @param string $sectionName + * + * @return RESTfmMessageSectionInterface + */ + public function getSection ($sectionName) { + $section = new RESTfmMessageSection; + $section->_setName($sectionName); + $sectionRows = $section->getRowsReference(); + + switch ($sectionName) { + case 'meta': + $section->_setDimensions = 2; + foreach ($this->_records as $record) { + $sectionRows[] = &$record->_getMetaReference(); + } + return $section; + break; + + case 'data': + $section->_setDimensions = 2; + foreach ($this->_records as $record) { + $sectionRows[] = &$record->_getDataReference(); + } + return $section; + break; + + case 'info': + $section->_setDimensions = 1; + $sectionRows[] = &$this->_info; + return $section; + break; + + case 'metaField': + $section->_setDimensions = 2; + foreach ($this->_metaFields as $row) { + $sectionRows[] = &$row->_getDataReference(); + } + return $section; + break; + + case 'multistatus': + $section->_setDimensions = 2; + foreach ($this->_multistatus as $row) { + $sectionRows[] = &$row->_getMultistatusReference(); + } + return $section; + break; + + case 'nav': + $section->_setDimensions = 2; + foreach ($this->_navs as $row) { + $sectionRows[] = &$row->_getDataReference(); + } + return $section; + break; + } + } + + /** + * Make a human readable string from stored contents. + * + * @return string + */ + public function __toString () { + + } +} diff --git a/lib/RESTfm/RESTfmMessageInterface.php b/lib/RESTfm/RESTfmMessageInterface.php index 65e50b4..6e0f9c4 100644 --- a/lib/RESTfm/RESTfmMessageInterface.php +++ b/lib/RESTfm/RESTfmMessageInterface.php @@ -29,14 +29,28 @@ public function getData (); /** * @param array $assocArray - * Set/update row data with provided array of key/value pairs. + * Set row data with provided array of key/value pairs. */ public function setData ($assocArray); /** - * @return integer Unique index of this row. + * @param string $fieldName + * + * @return mixed + */ + public function getField ($fieldName); + + /** + * @param string $fieldName + * @param mixed $fieldValue */ - public function getRowIndex (); + public function setField ($fieldName, $fieldValue); + + /** + * @param string $fieldName + */ + public function unsetField ($fieldName); + } /** @@ -77,9 +91,10 @@ interface RESTfmMessageMultistatusInterface { public function getIndex (); /** - * @param integer $index + * @param integer $dataRowIndex + * Index of row in request's data section that caused the error. */ - public function setIndex ($index); + public function setIndex ($dataRowIndex); /** * @return string From b9cb3ba2d7aba271730ddadb431c002921ac6799 Mon Sep 17 00:00:00 2001 From: Gavin Stewart Date: Thu, 9 Jun 2016 11:53:30 +1000 Subject: [PATCH 04/83] RESTfmMessage - WIP --- lib/RESTfm/RESTfmMessage.php | 135 ++++++++++++++++++++------ lib/RESTfm/RESTfmMessageInterface.php | 40 +++++++- 2 files changed, 145 insertions(+), 30 deletions(-) diff --git a/lib/RESTfm/RESTfmMessage.php b/lib/RESTfm/RESTfmMessage.php index 0bfbda6..6be5e46 100644 --- a/lib/RESTfm/RESTfmMessage.php +++ b/lib/RESTfm/RESTfmMessage.php @@ -213,25 +213,40 @@ public function getRows () { /** * RESTfmMessage internal function. */ - public function _setName ($name) { + public function __construct ($name, $dimensions) { $this->_name = $name; + $this->_dimensions = $dimensions; } /** * RESTfmMessage internal function. */ - public function _setDimensions ($dimensions) { - $this->_dimensions = $dimensions; + public function &_getRowsReference () { + return $this->_rows; } } class RESTfmMessage implements RESTfmMessageInterface { - protected $_info = array(); - protected $_metaFields = array(); - protected $_multistatus = array(); - protected $_navs = array(); - protected $_records = array(); + // -- Sections -- // + + protected $_info = array(); /// @var array 1 dimensional. + protected $_metaFields = array(); /// @var array of RESTfmMessageRow + protected $_multistatus = array(); /// @var array of RESTfmMessageMultistatus + protected $_navs = array(); /// @var array of RESTfmMessageRow + protected $_records = array(); /// @var array of RESTfmMessageRecordInterface + + /** + * @var array of known section names. + */ + protected $_knownSections = array('meta', 'data', 'info', + 'metaField', 'multistatus', 'nav'); + + /** + * @var associative array of recordId -> record index + * for identifying $_records[] by recordId. + */ + protected $_recordIdMap = array(); // --- Access methods for inserting/manipulating data --- // @@ -308,6 +323,12 @@ public function getNavs () { */ public function addRecord (RESTfmMessageRecordInterface $record) { $this->_records[] = $record; + + $recordId = $record->getRecordId(); + if ($recordId !== NULL) { + $recordIndex = count($this->_records) - 1; + $this->_recordIdMap[$recordId] = $recordIndex; + } } /** @@ -317,28 +338,40 @@ public function getRecords () { return $this->_records; } + /** + * Return a single record identified by $recordId + * + * @param string $recordId + * + * @return RESTfmMessageRecordInterface or NULL if $recordId does not exist. + */ + public function getRecordByRecordId ($recordId) { + if (isset($this->_recordIdMap[$recordId])) { + return $this->_recordIdMap[$recordId]; + } + } + // --- Access methods for reading data as sections (export formats) --- // /** * @return array of strings of available section names. - * Section names are: nav, data, meta, metaField, info, multistatus - * + * Section names are: meta, data, info, metaField, multistatus, nav */ public function getSectionNames () { $availableSections = array(); // Sort as 'meta', 'data', 'info', . if (!empty($this->_records)) { - $availableSections['meta'] = TRUE; - $availableSections['data'] = TRUE; + $availableSections[] = 'meta'; + $availableSections[] = 'data'; } - if (!empty($this->_info)) { $availableSections['info'] = TRUE; } - if (!empty($this->_metaFields)) { $availableSections['metaField'] = TRUE; } - if (!empty($this->_multistatus)) { $availableSections['multistatus'] = TRUE; } - if (!empty($this->_navs)) { $availableSections['nav'] = TRUE; } + if (!empty($this->_info)) { $availableSections[] = 'info'; } + if (!empty($this->_metaFields)) { $availableSections[] = 'metaField'; } + if (!empty($this->_multistatus)) { $availableSections[] = 'multistatus'; } + if (!empty($this->_navs)) { $availableSections[] = 'nav'; } - return array_keys($availableSections); + return $availableSections; } /** @@ -347,13 +380,11 @@ public function getSectionNames () { * @return RESTfmMessageSectionInterface */ public function getSection ($sectionName) { - $section = new RESTfmMessageSection; - $section->_setName($sectionName); - $sectionRows = $section->getRowsReference(); switch ($sectionName) { case 'meta': - $section->_setDimensions = 2; + $section = new RESTfmMessageSection($sectionName, 2); + $sectionRows = &$section->_getRowsReference(); foreach ($this->_records as $record) { $sectionRows[] = &$record->_getMetaReference(); } @@ -361,7 +392,8 @@ public function getSection ($sectionName) { break; case 'data': - $section->_setDimensions = 2; + $section = new RESTfmMessageSection($sectionName, 2); + $sectionRows = &$section->_getRowsReference(); foreach ($this->_records as $record) { $sectionRows[] = &$record->_getDataReference(); } @@ -369,13 +401,15 @@ public function getSection ($sectionName) { break; case 'info': - $section->_setDimensions = 1; + $section = new RESTfmMessageSection($sectionName, 1); + $sectionRows = &$section->_getRowsReference(); $sectionRows[] = &$this->_info; return $section; break; case 'metaField': - $section->_setDimensions = 2; + $section = new RESTfmMessageSection($sectionName, 2); + $sectionRows = &$section->_getRowsReference(); foreach ($this->_metaFields as $row) { $sectionRows[] = &$row->_getDataReference(); } @@ -383,7 +417,8 @@ public function getSection ($sectionName) { break; case 'multistatus': - $section->_setDimensions = 2; + $section = new RESTfmMessageSection($sectionName, 2); + $sectionRows = &$section->_getRowsReference(); foreach ($this->_multistatus as $row) { $sectionRows[] = &$row->_getMultistatusReference(); } @@ -391,7 +426,8 @@ public function getSection ($sectionName) { break; case 'nav': - $section->_setDimensions = 2; + $section = new RESTfmMessageSection($sectionName, 2); + $sectionRows = &$section->_getRowsReference(); foreach ($this->_navs as $row) { $sectionRows[] = &$row->_getDataReference(); } @@ -401,7 +437,52 @@ public function getSection ($sectionName) { } /** - * Make a human readable string from stored contents. + * @return associative array of all sections and data. + * With section(s) in the mixed form(s) of: + * 1 dimensional: + * array('sectionNameX' => array('key' => 'val', ...)) + * 2 dimensional: + * array('sectionNameY' => array( + * array('key' => 'val', ...) + * ... + * )) + */ + public function exportArray () { + $export = array(); + + foreach ($this->getSectionNames() as $sectionName) { + $sectionData = array(); + foreach ($this->getSection($sectionName) as $section) { + if ($section->getDimensions() == 1) { + $sectionRows = &$section->_getRowsreference(); + $sectionData = &$sectionRows[0]; + } elseif ($section->getDimensions() == 2) { + $sectionData = &$section->_getRowsreference(); + } + } + $export[] = array($sectionName => $sectionData); + } + + return $export; + } + + /** + * @param associative array $array of section(s) and data. + * With section(s) in the mixed form(s) of: + * 1 dimensional: + * array('sectionNameX' => array('key' => 'val', ...)) + * 2 dimensional: + * array('sectionNameY' => array( + * array('key' => 'val', ...) + * ... + * )) + */ + public function importArray ($array) { + + } + + /** + * Make a human readable string of all sections and data. * * @return string */ diff --git a/lib/RESTfm/RESTfmMessageInterface.php b/lib/RESTfm/RESTfmMessageInterface.php index 6e0f9c4..9742ba5 100644 --- a/lib/RESTfm/RESTfmMessageInterface.php +++ b/lib/RESTfm/RESTfmMessageInterface.php @@ -235,13 +235,21 @@ public function addRecord (RESTfmMessageRecordInterface $record); */ public function getRecords (); + /** + * Return a single record identified by $recordId + * + * @param string $recordId + * + * @return RESTfmMessageRecordInterface or NULL if $recordId does not exist. + */ + public function getRecordByRecordId ($recordId); + // --- Access methods for reading data as sections (export formats) --- // /** * @return array of strings of available section names. - * Section names are: nav, data, meta, metaField, info, multistatus - * + * Section names are: meta, data, info, metaField, multistatus, nav */ public function getSectionNames (); @@ -253,7 +261,33 @@ public function getSectionNames (); public function getSection ($name); /** - * Make a human readable string from stored contents. + * @return associative array of all sections and data. + * With section(s) in the mixed form(s) of: + * 1 dimensional: + * array('sectionNameX' => array('key' => 'val', ...)) + * 2 dimensional: + * array('sectionNameY' => array( + * array('key' => 'val', ...) + * ... + * )) + */ + public function exportArray (); + + /** + * @param associative array $array of section(s) and data. + * With section(s) in the mixed form(s) of: + * 1 dimensional: + * array('sectionNameX' => array('key' => 'val', ...)) + * 2 dimensional: + * array('sectionNameY' => array( + * array('key' => 'val', ...) + * ... + * )) + */ + public function importArray ($array); + + /** + * Make a human readable string of all sections and data. * * @return string */ From 1c6408b919107025bc0cf2e06ee9b4fc5d9dd139 Mon Sep 17 00:00:00 2001 From: Gavin Stewart Date: Fri, 17 Jun 2016 13:59:49 +1000 Subject: [PATCH 05/83] RESTfmMessage - complete implementation. --- lib/RESTfm/RESTfmMessage.php | 178 ++++++++++++++++++++++---- lib/RESTfm/RESTfmMessageInterface.php | 35 ++++- 2 files changed, 185 insertions(+), 28 deletions(-) diff --git a/lib/RESTfm/RESTfmMessage.php b/lib/RESTfm/RESTfmMessage.php index 6be5e46..2f6cb20 100644 --- a/lib/RESTfm/RESTfmMessage.php +++ b/lib/RESTfm/RESTfmMessage.php @@ -200,11 +200,19 @@ public function getName () { } /** - * Returns an array of one or more message rows. + * Returns an array of one or more rows. * Note: A section with only one dimension has only one row. * Note: A section with two dimensions may have more than one row. * - * @return array of RESTfmMessageRowInterface. + * @return array of section data in the form of: + * 1 dimensional: + * array('key' => 'val', ...) + * OR + * 2 dimensional: + * array( + * array('key' => 'val', ...), + * ... + * )) */ public function getRows () { return $this->_rows; @@ -270,9 +278,9 @@ public function getInfo () { /** * Add a message row object to 'metaField' section. * - * @param RESTfmMessageRowInterface $metaField + * @param RESTfmMessageRow $metaField */ - public function addMetaField (RESTfmMessageRowInterface $metaField) { + public function addMetaField (RESTfmMessageRow $metaField) { $this->_metaFields[] = $metaField; } @@ -286,9 +294,9 @@ public function getMetaFields () { /** * Add a message multistatus object to 'multistatus' section. * - * @param RESTfmMessageMultistatusInterface $multistatus + * @param RESTfmMessageMultistatus $multistatus */ - public function addMultistatus (RESTfmMessageMultistatusInterface $multistatus) { + public function addMultistatus (RESTfmMessageMultistatus $multistatus) { $this->_multistatus[] = $multistatus; } @@ -302,9 +310,9 @@ public function getMultistatus () { /** * Add a message row object to 'nav' section. * - * @param RESTfmMessageRowInterface $nav + * @param RESTfmMessageRow $nav */ - public function addNav (RESTfmMessageRowInterface $nav) { + public function addNav (RESTfmMessageRow $nav) { $this->_navs[] = $nav; } @@ -319,9 +327,9 @@ public function getNavs () { * Add a message record object that contains data for 'data' and 'meta' * sections. * - * @param RESTfmMessageRecordInterface $record + * @param RESTfmMessageRecord $record */ - public function addRecord (RESTfmMessageRecordInterface $record) { + public function addRecord (RESTfmMessageRecord $record) { $this->_records[] = $record; $recordId = $record->getRecordId(); @@ -380,7 +388,6 @@ public function getSectionNames () { * @return RESTfmMessageSectionInterface */ public function getSection ($sectionName) { - switch ($sectionName) { case 'meta': $section = new RESTfmMessageSection($sectionName, 2); @@ -436,6 +443,106 @@ public function getSection ($sectionName) { } } + /** + * @param string $sectionName section name. + * @param array of section data. + * With section data in the form of: + * 1 dimensional: + * array('key' => 'val', ...) + * OR + * 2 dimensional: + * array( + * array('key' => 'val', ...), + * ... + * )) + */ + public function setSection ($sectionName, $sectionData) { + switch ($sectionName) { + case 'meta': + $index = 0; + foreach ($sectionData as $rowIndex => $row) { + if (isset($this->_records[$index])) { + $record = $this->_records[$index]; + } else { + $record = new RESTfmMessageRecord(); + $this->addRecord($record); + } + foreach ($row as $key => $val) { + switch ($key) { + case 'href': + $record->setHref($val); + break; + case 'recordID': + $record->setRecordId($val); + $this->_recordIdMap[$val] = $index; + break; + } + } + $index++; + } + break; + + case 'data': + $index = 0; + foreach ($sectionData as $rowIndex => $row) { + if (isset($this->_records[$index])) { + $record = $this->_records[$index]; + } else { + $record = new RESTfmMessageRecord(); + $this->addRecord($record); + } + $record->setData($row); + $index++; + } + break; + + case 'info': + foreach ($sectionData as $key => $val) { + $this->_info[$key] = $val; + } + break; + + case 'metaField': + foreach ($sectionData as $rowIndex => $row) { + $metaField = new RESTfmMessageRow(); + $metaField->setData($row); + $this->addMetaField($metaField); + } + break; + + case 'multistatus': + foreach ($sectionData as $rowIndex => $row) { + $multistatus = new RESTfmMessageMultistatus(); + foreach ($row as $key => $val) { + switch ($key) { + case 'index': + $multistatus->setIndex($val); + break; + case 'Status': + $multistatus->setStatus($val); + break; + case 'Reason': + $multistatus->setReason($val); + break; + case 'recordID': + $multistatus->setRecordId($val); + break; + } + } + $this->addMultistatus($multistatus); + } + break; + + case 'nav': + foreach ($sectionData as $rowIndex => $row) { + $nav = new RESTfmMessageRow(); + $nav->setData($row); + $this->addNav($nav); + } + break; + } + } + /** * @return associative array of all sections and data. * With section(s) in the mixed form(s) of: @@ -443,7 +550,7 @@ public function getSection ($sectionName) { * array('sectionNameX' => array('key' => 'val', ...)) * 2 dimensional: * array('sectionNameY' => array( - * array('key' => 'val', ...) + * array('key' => 'val', ...), * ... * )) */ @@ -452,13 +559,12 @@ public function exportArray () { foreach ($this->getSectionNames() as $sectionName) { $sectionData = array(); - foreach ($this->getSection($sectionName) as $section) { - if ($section->getDimensions() == 1) { - $sectionRows = &$section->_getRowsreference(); - $sectionData = &$sectionRows[0]; - } elseif ($section->getDimensions() == 2) { - $sectionData = &$section->_getRowsreference(); - } + $section = $this->getSection($sectionName); + if ($section->getDimensions() == 1) { + $sectionRows = &$section->_getRowsreference(); + $sectionData = &$sectionRows[0]; + } elseif ($section->getDimensions() == 2) { + $sectionData = &$section->_getRowsreference(); } $export[] = array($sectionName => $sectionData); } @@ -473,12 +579,14 @@ public function exportArray () { * array('sectionNameX' => array('key' => 'val', ...)) * 2 dimensional: * array('sectionNameY' => array( - * array('key' => 'val', ...) + * array('key' => 'val', ...), * ... * )) */ public function importArray ($array) { - + foreach ($array as $sectionName => $sectionData) { + $this->setSection($sectionName, $sectionData); + } } /** @@ -487,6 +595,32 @@ public function importArray ($array) { * @return string */ public function __toString () { + $s = ''; + foreach ($this->getSectionNames() as $sectionName) { + $s .= $sectionName . ":\n"; + + $section = $this->getSection($sectionName); + if ($section->getDimensions() == 1) { + $sectionRows = &$section->_getRowsreference(); + $sectionData = &$sectionRows[0]; + foreach ($sectionData as $key => $value) { + $s .= ' ' . $key . '="' . addslashes($value) . '"' . "\n"; + } + } elseif ($section->getDimensions() == 2) { + $sectionData = &$section->_getRowsreference(); + foreach ($sectionData as $index => $row) { + $s .= ' ' . $index . ":\n"; + foreach ($row as $key => $value) { + $s .= ' ' . $key . '="' . addslashes($value) . '"' . "\n"; + } + } + } else { + $s .= ' ** Unknown format **.' . "\n"; + } + $s .= "\n"; + } + return $s; } -} + +}; diff --git a/lib/RESTfm/RESTfmMessageInterface.php b/lib/RESTfm/RESTfmMessageInterface.php index 9742ba5..c568de9 100644 --- a/lib/RESTfm/RESTfmMessageInterface.php +++ b/lib/RESTfm/RESTfmMessageInterface.php @@ -143,11 +143,19 @@ public function getDimensions (); public function getName (); /** - * Returns an array of one or more message rows. + * Returns an array of one or more rows. * Note: A section with only one dimension has only one row. * Note: A section with two dimensions may have more than one row. * - * @return array of RESTfmMessageRowInterface. + * @return array of section data in the form of: + * 1 dimensional: + * array('key' => 'val', ...) + * OR + * 2 dimensional: + * array( + * array('key' => 'val', ...), + * ... + * )) */ public function getRows (); } @@ -254,11 +262,26 @@ public function getRecordByRecordId ($recordId); public function getSectionNames (); /** - * @param string $name section name. + * @param string $sectionName * * @return RESTfmMessageSectionInterface */ - public function getSection ($name); + public function getSection ($sectionName); + + /** + * @param string $sectionName section name. + * @param array of section data. + * With section data in the form of: + * 1 dimensional: + * array('key' => 'val', ...) + * OR + * 2 dimensional: + * array( + * array('key' => 'val', ...), + * ... + * )) + */ + public function setSection ($sectionName, $sectionData); /** * @return associative array of all sections and data. @@ -267,7 +290,7 @@ public function getSection ($name); * array('sectionNameX' => array('key' => 'val', ...)) * 2 dimensional: * array('sectionNameY' => array( - * array('key' => 'val', ...) + * array('key' => 'val', ...), * ... * )) */ @@ -280,7 +303,7 @@ public function exportArray (); * array('sectionNameX' => array('key' => 'val', ...)) * 2 dimensional: * array('sectionNameY' => array( - * array('key' => 'val', ...) + * array('key' => 'val', ...), * ... * )) */ From 04ded06ce067434fa7de8b267ab294b1534b47a5 Mon Sep 17 00:00:00 2001 From: Gavin Stewart Date: Fri, 17 Jun 2016 14:31:54 +1000 Subject: [PATCH 06/83] Change version to GIT from UNKOWN for git clones (not packaged). --- js/demo.js | 2 +- lib/RESTfm/Version.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/demo.js b/js/demo.js index f941c1e..1b0feb9 100644 --- a/js/demo.js +++ b/js/demo.js @@ -639,7 +639,7 @@ RESTfm.checkVersion = function(serverVersion) { } if (RESTfm.version.indexOf('VERSION') >= 0 && - serverVersion.indexOf('UNKNOWN') >= 0 ) { + serverVersion.indexOf('GIT') >= 0 ) { return; } diff --git a/lib/RESTfm/Version.php b/lib/RESTfm/Version.php index dd2ba11..9b1594e 100644 --- a/lib/RESTfm/Version.php +++ b/lib/RESTfm/Version.php @@ -36,7 +36,7 @@ public static function getRevision() { public static function getVersion() { $revision = self::$_revision; if (strpos($revision, 'REVISION') !== FALSE) { - $revision = 'UNKNOWN'; + $revision = 'GIT'; } return self::$_release . '/' . $revision; From 7b6ce10610eb1220034721bce127f579dcb7fc6b Mon Sep 17 00:00:00 2001 From: Gavin Stewart Date: Fri, 17 Jun 2016 17:27:28 +1000 Subject: [PATCH 07/83] Add unit testing. RESTfmMessage unit testing - WIP --- PHPUnit/LICENSE | 33 + PHPUnit/phpunit | 1 + PHPUnit/phpunit-4.8.26.phar | 73927 +++++++++++++++++++++ RESTfm.php | 2 + lib/RESTfm/RESTfmMessage.php | 48 +- lib/RESTfm/RESTfmMessageInterface.php | 10 +- lib/autoload.php | 53 + libTest/RESTfmTest/RESTfmMessageTest.php | 196 + 8 files changed, 74254 insertions(+), 16 deletions(-) create mode 100644 PHPUnit/LICENSE create mode 120000 PHPUnit/phpunit create mode 100755 PHPUnit/phpunit-4.8.26.phar create mode 100644 lib/autoload.php create mode 100644 libTest/RESTfmTest/RESTfmMessageTest.php diff --git a/PHPUnit/LICENSE b/PHPUnit/LICENSE new file mode 100644 index 0000000..1a8c18d --- /dev/null +++ b/PHPUnit/LICENSE @@ -0,0 +1,33 @@ +PHPUnit + +Copyright (c) 2001-2016, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/PHPUnit/phpunit b/PHPUnit/phpunit new file mode 120000 index 0000000..464071f --- /dev/null +++ b/PHPUnit/phpunit @@ -0,0 +1 @@ +phpunit-4.8.26.phar \ No newline at end of file diff --git a/PHPUnit/phpunit-4.8.26.phar b/PHPUnit/phpunit-4.8.26.phar new file mode 100755 index 0000000..0c4206f --- /dev/null +++ b/PHPUnit/phpunit-4.8.26.phar @@ -0,0 +1,73927 @@ +#!/usr/bin/env php +')) { + fwrite( + STDERR, + 'This version of PHPUnit requires PHP 5.3.3; using the latest version of PHP is highly recommended.' . PHP_EOL + ); + + die(1); + } + + if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') { + print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt'); + exit; + } + + PHPUnit_TextUI_Command::main(); +} + +__HALT_COMPILER(); ?> + ¶?phpunit-4.8.26.phar<doctrine-instantiator/Doctrine/Instantiator/Instantiator.phpà …‹:Wà ü&à¶Rdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.phpÎ +…‹:WÎ +"Ÿè ¶Rdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.phpô…‹:WôhÅ7I¶Ldoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php¥…‹:W¥ó.ðöEdoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php~…‹:W~¶ÿÌ:¶doctrine-instantiator/LICENSE$…‹:W$ +Í‚å¶,dbunit/Extensions/Database/DefaultTester.php¨…‹:W¨ÚX`þ¶/dbunit/Extensions/Database/DB/TableMetaData.php…‹:Wª;Е¶'dbunit/Extensions/Database/DB/Table.php¢…‹:W¢8k8o¶+dbunit/Extensions/Database/DB/IMetaData.php3…‹:W3@G‰®¶5dbunit/Extensions/Database/DB/IDatabaseConnection.php …‹:W ¨dʶ1dbunit/Extensions/Database/DB/MetaData/SqlSrv.php’ …‹:W’ ¸¬÷¶1dbunit/Extensions/Database/DB/MetaData/Sqlite.php3 +…‹:W3 +½-Ô²¶0dbunit/Extensions/Database/DB/MetaData/PgSQL.php´…‹:W´ÌøÓ¶0dbunit/Extensions/Database/DB/MetaData/MySQL.phpÌ…‹:WÌ7-÷5¶3dbunit/Extensions/Database/DB/MetaData/Firebird.php…‹:Wǯ`¶<dbunit/Extensions/Database/DB/MetaData/InformationSchema.phpi…‹:WiœTÏ™¶0dbunit/Extensions/Database/DB/MetaData/Dblib.php …‹:W ÷¤Qü¶.dbunit/Extensions/Database/DB/MetaData/Oci.phpb…‹:WbÖÁ_í¶1dbunit/Extensions/Database/DB/FilteredDataSet.phpF…‹:WF (÷e¶/dbunit/Extensions/Database/DB/TableIterator.php} +…‹:W} +0WŠ6¶*dbunit/Extensions/Database/DB/MetaData.php¹…‹:W¹E±T¶)dbunit/Extensions/Database/DB/DataSet.phpR…‹:WRÄxÁ¶0dbunit/Extensions/Database/DB/ResultSetTable.phpè…‹:WèÍ›ã¶;dbunit/Extensions/Database/DB/DefaultDatabaseConnection.php}…‹:W}ô£Îø¶-dbunit/Extensions/Database/AbstractTester.php…‹:WCÖY¶'dbunit/Extensions/Database/TestCase.php"…‹:W"Ïoåj¶3dbunit/Extensions/Database/DataSet/DefaultTable.phpƒ…‹:Wƒ!¶÷t¶4dbunit/Extensions/Database/DataSet/DataSetFilter.phpj…‹:Wj)彶-dbunit/Extensions/Database/DataSet/ITable.php…‹:WȃN”¶5dbunit/Extensions/Database/DataSet/FlatXmlDataSet.phpO…‹:WOè36K¶9dbunit/Extensions/Database/DataSet/ReplacementDataSet.phpŠ +…‹:WŠ + Þÿ¶;dbunit/Extensions/Database/DataSet/DefaultTableIterator.php˜ …‹:W˜ 4Ùð¶2dbunit/Extensions/Database/DataSet/TableFilter.phpæ …‹:Wæ ×°¯h¶:dbunit/Extensions/Database/DataSet/Persistors/MysqlXml.phpÝ …‹:WÝ ŠOìÓ¶9dbunit/Extensions/Database/DataSet/Persistors/Factory.php…‹:WÚ(ý¶:dbunit/Extensions/Database/DataSet/Persistors/Abstract.php +…‹:W +D:ã¶6dbunit/Extensions/Database/DataSet/Persistors/Yaml.phpç…‹:Wçæ–¶9dbunit/Extensions/Database/DataSet/Persistors/FlatXml.phpŠ …‹:WŠ TÏZ,¶5dbunit/Extensions/Database/DataSet/Persistors/Xml.php5 …‹:W5 ‡‰.¶¶3dbunit/Extensions/Database/DataSet/IPersistable.phpŽ…‹:WŽ•±ˆ‡¶1dbunit/Extensions/Database/DataSet/XmlDataSet.phpˆ…‹:WˆJz¹–¶6dbunit/Extensions/Database/DataSet/MysqlXmlDataSet.php'…‹:W'J¤!¶7dbunit/Extensions/Database/DataSet/ReplacementTable.php|…‹:W|hRd¶;dbunit/Extensions/Database/DataSet/DefaultTableMetaData.php…‹:WÎ÷ض7dbunit/Extensions/Database/DataSet/CompositeDataSet.php: +…‹:W: +ë!© ¶3dbunit/Extensions/Database/DataSet/QueryDataSet.php° …‹:W° þ‘Vö,dbunit/Extensions/Database/DataSet/ISpec.php§…‹:W§Ý‚ï—¶4dbunit/Extensions/Database/DataSet/AbstractTable.phpX…‹:WXȹ¶1dbunit/Extensions/Database/DataSet/CsvDataSet.php …‹:W  ïÑä¶5dbunit/Extensions/Database/DataSet/ITableIterator.php…‹:W ®!-¶5dbunit/Extensions/Database/DataSet/DefaultDataSet.php¿…‹:W¿˜WÅJ¶5dbunit/Extensions/Database/DataSet/ITableMetaData.php1…‹:W1,È+;¶:dbunit/Extensions/Database/DataSet/TableMetaDataFilter.phpR …‹:WR ¨†@¶?dbunit/Extensions/Database/DataSet/ReplacementTableIterator.phpÜ …‹:WÜ Ú‹™»¶8dbunit/Extensions/Database/DataSet/SymfonyYamlParser.phpO…‹:WOðPW¸¶2dbunit/Extensions/Database/DataSet/YamlDataSet.php‹…‹:W‹¦õ¸_¶9dbunit/Extensions/Database/DataSet/AbstractXmlDataSet.phpþ …‹:Wþ Q6Ž¶6dbunit/Extensions/Database/DataSet/AbstractDataSet.php#…‹:W#[ÉGζ/dbunit/Extensions/Database/DataSet/IDataSet.php…‹:WµZ߶<dbunit/Extensions/Database/DataSet/AbstractTableMetaData.phpá…‹:Wá;¢c¼¶1dbunit/Extensions/Database/DataSet/QueryTable.php`…‹:W`Á¾ ¸¶3dbunit/Extensions/Database/DataSet/ArrayDataSet.phpy…‹:Wy[ƒ¶2dbunit/Extensions/Database/DataSet/IYamlParser.php…‹:W@Ûˆ[¶0dbunit/Extensions/Database/DataSet/Specs/Csv.php… +…‹:W… +¹¾õ¶5dbunit/Extensions/Database/DataSet/Specs/IFactory.phpl…‹:WlÕ—½+¶4dbunit/Extensions/Database/DataSet/Specs/Factory.phpÚ…‹:WÚAä‚̶1dbunit/Extensions/Database/DataSet/Specs/Yaml.phpÑ…‹:WÑ{;>à¶4dbunit/Extensions/Database/DataSet/Specs/FlatXml.phpè…‹:Wè½ß†¶4dbunit/Extensions/Database/DataSet/Specs/DbTable.php¢…‹:W¢‡°ãJ¶4dbunit/Extensions/Database/DataSet/Specs/DbQuery.php …‹:W Êwà¶0dbunit/Extensions/Database/DataSet/Specs/Xml.phpÊ…‹:WÊ•TZ¶&dbunit/Extensions/Database/ITester.php£…‹:W£‚Ó#ž¶4dbunit/Extensions/Database/IDatabaseListConsumer.php;…‹:W;4o¶8dbunit/Extensions/Database/Constraint/DataSetIsEqual.php…‹:WMﱶ6dbunit/Extensions/Database/Constraint/TableIsEqual.php…‹:W7Š +¶7dbunit/Extensions/Database/Constraint/TableRowCount.php“…‹:W“IåXÓ¶6dbunit/Extensions/Database/UI/InvalidModeException.php…‹:W*,å¶-dbunit/Extensions/Database/UI/ModeFactory.phpB +…‹:WB +ßÒ£«¶)dbunit/Extensions/Database/UI/Command.phpI…‹:WI¹T™¶)dbunit/Extensions/Database/UI/Context.php…‹:W¡G¶)dbunit/Extensions/Database/UI/IMedium.phpP…‹:WPŒ¸Ôš¶'dbunit/Extensions/Database/UI/IMode.phpÜ…‹:Wܼ¶d¶.dbunit/Extensions/Database/UI/IModeFactory.php +…‹:W +Ÿ½Çø¶0dbunit/Extensions/Database/UI/IMediumPrinter.php¦…‹:W¦¼}Ù0¶.dbunit/Extensions/Database/UI/Mediums/Text.phpŸ …‹:WŸ o4Äa¶5dbunit/Extensions/Database/UI/Modes/ExportDataSet.php² …‹:W² ’{‰¶?dbunit/Extensions/Database/UI/Modes/ExportDataSet/Arguments.php +…‹:W +(ΰù¶/dbunit/Extensions/Database/Operation/Insert.phpÖ…‹:WÖ…EP¶2dbunit/Extensions/Database/Operation/Composite.phpˆ…‹:Wˆµ)ʶ0dbunit/Extensions/Database/Operation/Replace.phpe…‹:WeOÇئ¶1dbunit/Extensions/Database/Operation/Truncate.php¹ …‹:W¹ «rCŠ¶0dbunit/Extensions/Database/Operation/Factory.php …‹:W ¡ ¤Ì¶-dbunit/Extensions/Database/Operation/Null.php‡…‹:W‡¦û0Ó¶1dbunit/Extensions/Database/Operation/RowBased.php5…‹:W5iÁ¿h¶/dbunit/Extensions/Database/Operation/Update.php¡…‹:W¡µ>%¶;dbunit/Extensions/Database/Operation/IDatabaseOperation.phpÔ…‹:WÔ"Åñ¶2dbunit/Extensions/Database/Operation/DeleteAll.php›…‹:W›ŒÉCi¶/dbunit/Extensions/Database/Operation/Delete.php…‹:WnÙÛ¶2dbunit/Extensions/Database/Operation/Exception.php/…‹:W/£J¾¶(dbunit/Extensions/Database/Exception.php…‹:WmŽ-`¶sebastian-diff/Line.phpŸ…‹:WŸî®:;¶sebastian-diff/LICENSE…‹:WvEvösebastian-diff/Chunk.phpÎ…‹:WÎ-*¶sebastian-diff/Diff.php±…‹:W±˾׶sebastian-diff/Differ.phpÜ…‹:WÜ“’`¶Lsebastian-diff/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php¯ …‹:W¯ ÿ_¶Jsebastian-diff/LCS/TimeEfficientLongestCommonSubsequenceImplementation.phpœ…‹:Wœf#5/¶/sebastian-diff/LCS/LongestCommonSubsequence.phpl…‹:Wlõlö1¶sebastian-diff/Parser.php' …‹:W' ÐZµK¶sebastian-environment/LICENSE +…‹:W +¶îá߶!sebastian-environment/Runtime.phpH…‹:WH8‡¨¶!sebastian-environment/Console.phpž …‹:Wž Oh5¶sebastian-global-state/LICENSE +…‹:W + `¶'sebastian-global-state/CodeExporter.php›…‹:W›`Ö(C¶#sebastian-global-state/Restorer.php§…‹:W§Ó“;\¶+sebastian-global-state/RuntimeException.phpq…‹:Wq¿~]!¶$sebastian-global-state/Blacklist.php[ …‹:W[ :®¶#sebastian-global-state/Snapshot.phpå%…‹:Wå%ÖSݶ$sebastian-global-state/Exception.php?…‹:W?ɶ manifest.txta…‹:Waƒ³ŒÚ¶3sebastian-comparator/SplObjectStorageComparator.php +…‹:W +çMÕQ¶)sebastian-comparator/ObjectComparator.php¡…‹:W¡ƒèíw¶sebastian-comparator/LICENSE …‹:W ”:¶ sebastian-comparator/Factory.phpd …‹:Wd Ù‡1¶)sebastian-comparator/ScalarComparator.php>…‹:W>¾»B(¶,sebastian-comparator/ExceptionComparator.php×…‹:W× kf¶+sebastian-comparator/DateTimeComparator.php“ …‹:W“ ìä¶*sebastian-comparator/DOMNodeComparator.php“ …‹:W“ ÅÙ>%¶+sebastian-comparator/ResourceComparator.phpñ…‹:Wñ&¤w¶(sebastian-comparator/ArrayComparator.phpé…‹:WévËx¶*sebastian-comparator/ComparisonFailure.php€ …‹:W€ V¶'sebastian-comparator/TypeComparator.php§…‹:W§ËQ¶)sebastian-comparator/DoubleComparator.php7…‹:W7S¬tÙ¶-sebastian-comparator/MockObjectComparator.php´…‹:W´–îO¶*sebastian-comparator/NumericComparator.php~ +…‹:W~ +‡7Þà¶#sebastian-comparator/Comparator.phpP…‹:WP!P‹=¶symfony/LICENSE)…‹:W)ë&•¶1symfony/yaml/Symfony/Component/Yaml/Unescaper.php—…‹:W—‰ 6ä¶,symfony/yaml/Symfony/Component/Yaml/Yaml.php¶ …‹:W¶ ¡G‘¶?symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.phpÒ…‹:WÒؙ՚¶Bsymfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.phpð…‹:WðÏ|-¶@symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php…‹:WÎ79‹¶Dsymfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.phpÆ…‹:WÆî+­l¶/symfony/yaml/Symfony/Component/Yaml/Escaper.php…‹:WK‹ã¶.symfony/yaml/Symfony/Component/Yaml/Parser.php‡h…‹:W‡h.©ñ¶.symfony/yaml/Symfony/Component/Yaml/Inline.phpbM…‹:WbMïoÜ—¶.symfony/yaml/Symfony/Component/Yaml/Dumper.php …‹:W l£D¶#sebastian-recursion-context/LICENSE…‹:WÉðζ8sebastian-recursion-context/InvalidArgumentException.php’…‹:W’mH¶'sebastian-recursion-context/Context.php¥…‹:W¥øqP¾¶)sebastian-recursion-context/Exception.phpJ…‹:WJÈô³ñ¶php-invoker/Invoker.phpï…‹:Wï wචphp-invoker/TimeoutException.phpp…‹:Wp~ªø¶phpunit/TextUI/Command.phpŒ……‹:WŒ…è¸Ù¶ phpunit/TextUI/ResultPrinter.phpåC…‹:WåC³g“o¶phpunit/TextUI/TestRunner.phpU¨…‹:WU¨IkO߶phpunit/Util/Regex.phpå…‹:Wå±PÏd¶phpunit/Util/String.php…‹:W‡íàd¶phpunit/Util/PHP.phpÞ…‹:WÞrüÚ†¶phpunit/Util/Filesystem.php–…‹:W–w¡­¶phpunit/Util/Test.phpÞ……‹:WÞ…Óÿ:¶phpunit/Util/Fileloader.phpâ…‹:WâÅÉú¶phpunit/Util/Type.php…‹:WCÜ!Ò¶phpunit/Util/Configuration.phpb‹…‹:Wb‹æÐ~¶&phpunit/Util/InvalidArgumentHelper.phpS…‹:WS(QÏ©¶phpunit/Util/Filter.phpÛ …‹:WÛ -u0t¶phpunit/Util/Log/JSON.php,…‹:W,/ŠJP¶phpunit/Util/Log/TAP.php …‹:W ÊÄ=ª¶phpunit/Util/Log/JUnit.php³2…‹:W³2åY%f¶phpunit/Util/Getopt.php»…‹:W»}h¶phpunit/Util/PHP/Windows.php^ +…‹:W^ +ƒ€?d¶phpunit/Util/PHP/eval-stdin.php6…‹:W6ÍŒG¶1phpunit/Util/PHP/Template/TestCaseMethod.tpl.distR +…‹:WR +V^b¶phpunit/Util/PHP/Default.php,…‹:W,ýV¶phpunit/Util/GlobalState.php/…‹:W/KU|¶"phpunit/Util/TestSuiteIterator.phpû…‹:WûÏ☶phpunit/Util/ErrorHandler.php‡ …‹:W‡ #ù¾ô¶phpunit/Util/Blacklist.phpÒ …‹:WÒ \¿+ö¶+phpunit/Util/TestDox/ResultPrinter/Text.phpw…‹:Ww ;ÿ›¶+phpunit/Util/TestDox/ResultPrinter/HTML.phpª…‹:WªpfµÊ¶&phpunit/Util/TestDox/ResultPrinter.phpk…‹:Wk`îýB¶'phpunit/Util/TestDox/NamePrettifier.php0 …‹:W0 èê̶phpunit/Util/XML.phpu…‹:WuŽ¯ý¶phpunit/Util/Printer.phpt…‹:Wt19X^¶$phpunit/Extensions/TestDecorator.php@ …‹:W@ ÍÀ9¶#phpunit/Extensions/PhptTestCase.php3…‹:W3§>Òë¶%phpunit/Extensions/TicketListener.php%…‹:W%³"X¶$phpunit/Extensions/PhptTestSuite.php…‹:W ²‚ܶ#phpunit/Extensions/RepeatedTest.php÷…‹:W÷€ûû¶%phpunit/Extensions/GroupTestSuite.php‡…‹:W‡o`˶"phpunit/Framework/TestListener.phpn +…‹:Wn +{_µV¶&phpunit/Framework/Error/Deprecated.phpF…‹:WF´¢V¶#phpunit/Framework/Error/Warning.php3…‹:W3mO‘¤¶"phpunit/Framework/Error/Notice.php0…‹:W0ÜIŠ›¶phpunit/Framework/Warning.phpÁ…‹:WÁþJk¶2phpunit/Framework/InvalidCoversTargetException.php„…‹:W„%M×à¶phpunit/Framework/Test.php”…‹:W”…ä£%¶,phpunit/Framework/TestSuite/DataProvider.phpƒ…‹:Wƒ‚g¶)phpunit/Framework/Constraint/LessThan.php¾…‹:W¾4¤w϶1phpunit/Framework/Constraint/ExceptionMessage.phpC…‹:WCæø”€¶,phpunit/Framework/Constraint/GreaterThan.phpÇ…‹:WÇÅOºR¶7phpunit/Framework/Constraint/ExceptionMessageRegExp.php`…‹:W`ÓÃ0}¶)phpunit/Framework/Constraint/Callback.php3…‹:W3¥ãí¶'phpunit/Framework/Constraint/IsJson.php—…‹:W—z#l:¶,phpunit/Framework/Constraint/ArrayHasKey.php¸…‹:W¸V†Ë¶(phpunit/Framework/Constraint/IsFalse.phpu…‹:WuÖ»¬ ¶+phpunit/Framework/Constraint/FileExists.php³…‹:W³+“2¶1phpunit/Framework/Constraint/StringStartsWith.phpÆ…‹:WÆ­âƒ[¶Aphpunit/Framework/Constraint/JsonMatches/ErrorMessageProvider.php¯…‹:W¯d˜¶*phpunit/Framework/Constraint/PCREMatch.php½…‹:W½¹Žªˆ¶2phpunit/Framework/Constraint/ClassHasAttribute.phpÃ…‹:WÃçb¶.phpunit/Framework/Constraint/StringMatches.php …‹:W %½Àc¶.phpunit/Framework/Constraint/ExceptionCode.php[…‹:W[¤ø ¶)phpunit/Framework/Constraint/SameSize.phpS…‹:WS/#ÏS¶+phpunit/Framework/Constraint/IsAnything.phpp…‹:Wpù–V¶'phpunit/Framework/Constraint/IsType.phpt …‹:Wt KÒP’¶/phpunit/Framework/Constraint/StringContains.php2…‹:W2›]ã.¶*phpunit/Framework/Constraint/Attribute.phpš …‹:Wš l›øì¶3phpunit/Framework/Constraint/ObjectHasAttribute.php…‹:W‚£/¶'phpunit/Framework/Constraint/IsTrue.phpq…‹:Wq*%Ïö¶,phpunit/Framework/Constraint/JsonMatches.php …‹:W ö·Ò¶8phpunit/Framework/Constraint/TraversableContainsOnly.php­ …‹:W­ g|s¶(phpunit/Framework/Constraint/IsEqual.php!…‹:W! (Q¶$phpunit/Framework/Constraint/Not.php™…‹:W™ˆRìk¶$phpunit/Framework/Constraint/And.php6 …‹:W6 æÛõŶ8phpunit/Framework/Constraint/ClassHasStaticAttribute.php„…‹:W„¡Ê ‘¶#phpunit/Framework/Constraint/Or.phpf …‹:Wf  ¼¢&¶*phpunit/Framework/Constraint/Exception.phpc…‹:Wc–*/¶'phpunit/Framework/Constraint/IsNull.phpq…‹:Wqœ^ô`¶+phpunit/Framework/CodeCoverageException.phpq…‹:WqE´•í¶(phpunit/Framework/IncompleteTestCase.php5…‹:W5.–Sž¶)phpunit/Framework/IncompleteTestError.php…‹:WHtº ¶phpunit/Framework/Error.php$…‹:W$X%yW¶&phpunit/Framework/Assert/Functions.phpù…‹:Wùg=G/¶$phpunit/Framework/IncompleteTest.phpä…‹:WäòJì¶0phpunit/Framework/ExpectationFailedException.php™…‹:W™_Sõ;¶phpunit/Framework/Exception.php …‹:W à~bõ¶%phpunit/Framework/SkippedTestCase.phpê…‹:Wê`¬uà¶phpunit/Runner/Version.phpå…‹:Wå»ံ!phpunit/Runner/BaseTestRunner.phpZ…‹:WZÁ×øc¶'phpunit/Runner/Filter/Group/Exclude.phpâ…‹:Wâ„ÊPF¶'phpunit/Runner/Filter/Group/Include.phpá…‹:WáVe¶phpunit/Runner/Filter/Test.php …‹:W aŸŽ¶!phpunit/Runner/Filter/Factory.phpÜ…‹:WÜ…õJ¶phpunit/Runner/Filter/Group.php…‹:Wc2{m¶"phpunit/Runner/TestSuiteLoader.phpä…‹:WäŒ÷¶*phpunit/Runner/StandardTestSuiteLoader.phpÌ …‹:WÌ –À‰ ¶phpunit/Runner/Exception.phpt…‹:Wt€d§¿¶phpunit/Exception.phps…‹:Wsy:ƒÛ¶sebastian-version/Version.php2…‹:W2BŸâZ¶sebastian-version/LICENSE…‹:Wn¶0phpspec-prophecy/Prophecy/Comparator/Factory.phpÔ…‹:WÔÖˆi§¶;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.phps…‹:Ws¤hǶ:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.phpK…‹:WK)RQã¶5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php‡…‹:W‡ß5D¶/phpspec-prophecy/Prophecy/Prophecy/Revealer.php…‹:WjÉɸ¶8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php,…‹:W,¡W¶8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpH…‹:WH‡gZ¶¶5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php_,…‹:W_,¢KÈÁ¶?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.phpß…‹:Wßi²¶;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'…‹:W'â(«³¶3phpspec-prophecy/Prophecy/Promise/ReturnPromise.php…‹:WçØä¶5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php…‹:W[žÜ¶6phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpK…‹:WK¾¬…ë¶2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php` +…‹:W` ++­,¶8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php4 …‹:W4 A;K2¶Bphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php‡…‹:W‡#Iú¶@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.phpü…‹:Wü‰ÑÊ>¶;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php…‹:WÙ°Š¼¶:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php«…‹:W«Fh¶<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.phpá…‹:Wá‚4®Ì¶@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.phpæ…‹:Wæ¯Êý¶:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php,…‹:W,cRÊ̶Aphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php’…‹:W’pbø¶<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php”…‹:W”ÞJú:¶<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php…‹:Wܼr¶6phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php¥…‹:W¥®næ\¶<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php¡ …‹:W¡ …3¶;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.phpÚ…‹:WÚÖbN/¶<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.phpø…‹:Wø Nãv¶=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php9 +…‹:W9 +_ÛJg¶'phpspec-prophecy/Prophecy/Call/Call.phpÓ …‹:WÓ {:å%¶-phpspec-prophecy/Prophecy/Call/CallCenter.php…‹:Wå|·¶7phpspec-prophecy/Prophecy/Prediction/CallPrediction.phpQ …‹:WQ Iæ“é¶;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php“…‹:W“Vb{ζ:phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.phpå…‹:WåL9%¶<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php“ …‹:W“ XŠü¶<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.phpŸ…‹:WŸ`IE¶-phpspec-prophecy/Prophecy/Util/StringUtil.phpì …‹:Wì %ø¶-phpspec-prophecy/Prophecy/Util/ExportUtil.php2…‹:W2áWÞî¶%phpspec-prophecy/Prophecy/Prophet.php…‹:WçvqŽ¶Bphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php™…‹:W™Üê$϶Hphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php)…‹:W)Fù¢4¶Hphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php…‹:W†:‚F¶Dphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.phpÖ…‹:WÖ£Áó¸¶Ephpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php»…‹:W»?D<ζKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php,…‹:W,õa¶Fphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php…‹:W2T¢Ñ¶Pphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php…‹:Wæ ƶLphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpJ…‹:WJ~ÐãD¶Cphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.phpÚ…‹:WÚÁl<¶@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php•…‹:W•¨gì¶Gphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.phpÖ…‹:WÖiÞhó¶Dphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.phpª…‹:WªÛ‰?¶?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php©…‹:W©zéFƒ¶@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php—…‹:W—ÃZ^¶Jphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php…‹:Wýª¶Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.phpÅ…‹:WÅh+Jphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.phpà…‹:Wàõ¡…ý¶Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php­…‹:W­á +жEphpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.phpµ…‹:Wµ77/%¶1phpspec-prophecy/Prophecy/Exception/Exception.php+…‹:W+¸µ‘¶&phpspec-prophecy/Prophecy/Argument.phpÇ…‹:WÇAT¶Bphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php7 …‹:W7 ’О¤¶<phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.phpÔ…‹:WÔä?Br¶;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php‡…‹:W‡Õ•„c¶>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.phpI…‹:WIÐ)Uݶ?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.phpµ…‹:Wµ'à¢×¶Aphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.phpâ…‹:Wâ¤>úX¶Cphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.phpò…‹:Wòçûªå¶-phpspec-prophecy/Prophecy/Doubler/Doubler.php…‹:W8]Õ^¶3phpspec-prophecy/Prophecy/Doubler/NameGenerator.phpŠ…‹:WŠõÑ7¶0phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpF …‹:WF ¼ël¦¶3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.phpƒ…‹:Wƒ̇gè¶5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.phpâ…‹:Wâ8dj¶=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php½ …‹:W½ û/@ȶCphpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.phpÑ…‹:WÑx“Â^¶Ephpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.phpô +…‹:Wô +Ân–i¶Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php …‹:W §€jN¶Hphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php’…‹:W’:0`ò¶Dphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phpl…‹:Wl)Š5:¶?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.phpÓ…‹:WÓè!KM¶Pphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phpp…‹:Wpx¤¿ˆ¶Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php…‹:W!h^¶phpspec-prophecy/LICENSE}…‹:W}òÅ6¶1phpunit-selenium/Extensions/Selenium2TestCase.php!E…‹:W!Eï[‘ñ¶4phpunit-selenium/Extensions/SeleniumBrowserSuite.phpò…‹:WòD³¥¶>phpunit-selenium/Extensions/Selenium2TestCase/StateCommand.phpw +…‹:Ww + e3Z¶<phpunit-selenium/Extensions/Selenium2TestCase/KeysHolder.php…‹:WÑ|ÚH¶Aphpunit-selenium/Extensions/Selenium2TestCase/ElementCriteria.phpN …‹:WN ʶ@phpunit-selenium/Extensions/Selenium2TestCase/CommandsHolder.phpË…‹:WËZew¶6phpunit-selenium/Extensions/Selenium2TestCase/Keys.phpI…‹:WIî†Í¶:phpunit-selenium/Extensions/Selenium2TestCase/Response.php…‹:WTZ¶8phpunit-selenium/Extensions/Selenium2TestCase/Window.phpO …‹:WO S§ßô¶Dphpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Css.phpi …‹:Wi ó¶Lphpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/GenericPost.phpW +…‹:WW +ëLq¶Pphpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/GenericAccessor.phpi +…‹:Wi +•Pe¶Fphpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Value.phpG …‹:WG +Uç¶Gphpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Equals.php` …‹:W` Én±Õ¶Jphpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Attribute.phpt …‹:Wt  ˈ¶Fphpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Click.php+ +…‹:W+ +¡Ä»¶Dphpunit-selenium/Extensions/Selenium2TestCase/ScreenshotListener.phpØ…‹:WØh­7Ƕ9phpunit-selenium/Extensions/Selenium2TestCase/Command.phpL …‹:WL ÎcyƶDphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Log.php …‹:W œð"=¶Ephpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Keys.php$…‹:W$wîͶGphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Window.phpÜ +…‹:WÜ +•ˆ’¶Iphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Location.phpÏ …‹:WÏ ô¡Ë ¶Pphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/GenericAccessor.phpT +…‹:WT +X·ì¶Gphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Active.php + …‹:W + ×6¸¶Dphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Url.php …‹:W K¸Ž¶Lphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/AcceptAlert.php1 +…‹:W1 +BHǶLphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Orientation.phpÞ …‹:WÞ @\WI¶Qphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/GenericAttribute.phpŸ +…‹:WŸ +›Bí¶Fphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Frame.php¶ …‹:W¶ çz"¶Mphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/DismissAlert.php6 +…‹:W6 +Ñv’l¶Ephpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/File.phpè…‹:Wè0“ß—¶Jphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/AlertText.phpC …‹:WC lXim¶Fphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Click.php» …‹:W» ^ÿaé¶Gphpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/MoveTo.phpÎ…‹:WΣ- +¶Ephpunit-selenium/Extensions/Selenium2TestCase/NoSeleniumException.phpÅ …‹:WÅ Ûök¶9phpunit-selenium/Extensions/Selenium2TestCase/Session.php!1…‹:W!1š ™¶;phpunit-selenium/Extensions/Selenium2TestCase/WaitUntil.php”…‹:W”z“¶Hphpunit-selenium/Extensions/Selenium2TestCase/SessionStrategy/Shared.phpH…‹:WHÚVm’¶Jphpunit-selenium/Extensions/Selenium2TestCase/SessionStrategy/Isolated.php’ …‹:W’ ìͶ9phpunit-selenium/Extensions/Selenium2TestCase/Element.php …‹:W &éø¶Aphpunit-selenium/Extensions/Selenium2TestCase/SessionStrategy.php¡ …‹:W¡ ̳ãr¶8phpunit-selenium/Extensions/Selenium2TestCase/Driver.phpÑ…‹:WÑeÅ5v¶5phpunit-selenium/Extensions/Selenium2TestCase/URL.php™…‹:W™HFž¶Dphpunit-selenium/Extensions/Selenium2TestCase/WebDriverException.phpf …‹:Wf ÍK1ï¶Bphpunit-selenium/Extensions/Selenium2TestCase/Session/Timeouts.php]…‹:W]œ/X¶@phpunit-selenium/Extensions/Selenium2TestCase/Session/Cookie.phpý…‹:Wý$!¸–¶Aphpunit-selenium/Extensions/Selenium2TestCase/Session/Storage.php8 …‹:W8 ›‚º¶Hphpunit-selenium/Extensions/Selenium2TestCase/Session/Cookie/Builder.php…‹:WÓ¸q¶;phpunit-selenium/Extensions/Selenium2TestCase/Exception.phpã …‹:Wã H"Ç[¶@phpunit-selenium/Extensions/Selenium2TestCase/Element/Select.php­…‹:W­ëL@"¶Bphpunit-selenium/Extensions/Selenium2TestCase/Element/Accessor.php¯…‹:W¯û\w€¶1phpunit-selenium/Extensions/SeleniumTestSuite.php…‹:W®šä¶0phpunit-selenium/Extensions/SeleniumTestCase.phpÀ—…‹:WÀ—ÃvK¶?phpunit-selenium/Extensions/SeleniumCommon/phpunit_coverage.php`…‹:W`½ùOm¶5phpunit-selenium/Extensions/SeleniumCommon/append.php …‹:W ›·ú÷¶6phpunit-selenium/Extensions/SeleniumCommon/prepend.php? …‹:W? °·³¶=phpunit-selenium/Extensions/SeleniumCommon/RemoteCoverage.phpI…‹:WIÝ\´.¶:phpunit-selenium/Extensions/SeleniumCommon/ExitHandler.php*…‹:W*¢èÏ϶7phpunit-selenium/Extensions/SeleniumTestCase/Driver.phpM°…‹:WM°0âR¶)phpdocumentor-reflection-docblock/LICENSE8…‹:W8á‰Ê¶Kphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag.phpÛ(…‹:WÛ(ŒE6 ¶Rphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Serializer.php…‹:WòrжPphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Location.phpq…‹:WquÁ/¶Ophpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Context.php5…‹:W5l.%¶Uphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.phpÙ…‹:WÙpSž¶\phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php]…‹:W]Rp¶Uphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php~…‹:W~å!kͶSphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.phpE…‹:WE.„˶Uphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php …‹:W äÙn*¶Rphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/VarTag.phpE…‹:WEƒ•üͶUphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.phpM …‹:WM 1ôú¶Wphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.phpO…‹:WO«Î#m¶Yphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.php†…‹:W†K § ¶Sphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.phpL…‹:WLªF¶Uphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.phpL…‹:WLœ"â¶Vphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.phpx +…‹:Wx +ÓËEƶ[phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php[…‹:W[Í< =¶Tphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php|…‹:W|tøûR¶Tphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.phpH …‹:WH ±ñ ¶Uphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.phpI…‹:WI9¹{¶Rphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.phpÿ…‹:Wÿià?$¶Vphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.php™…‹:W™ £ +ï¶Wphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Type/Collection.php…‹:W+=4¶Sphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Description.php—…‹:W—ñԲضGphpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock.phpc6…‹:Wc6õËq¶"php-code-coverage/CodeCoverage.phpsi…‹:Wsið]¶-php-code-coverage/CodeCoverage/Report/PHP.php…‹:W4R}¶4php-code-coverage/CodeCoverage/Report/XML/Totals.phpÇ…‹:WÇo·F¶7php-code-coverage/CodeCoverage/Report/XML/Directory.php‰…‹:W‰ÑÒçZ¶3php-code-coverage/CodeCoverage/Report/XML/Tests.phpÖ…‹:WÖ2t¶9php-code-coverage/CodeCoverage/Report/XML/File/Report.phpC…‹:WC¨Y{ȶ9php-code-coverage/CodeCoverage/Report/XML/File/Method.phpu…‹:Wuèîʶ;php-code-coverage/CodeCoverage/Report/XML/File/Coverage.php5…‹:W5€(+R¶7php-code-coverage/CodeCoverage/Report/XML/File/Unit.phpB +…‹:WB + kr¶2php-code-coverage/CodeCoverage/Report/XML/File.php0…‹:W0ŠäÚ¶5php-code-coverage/CodeCoverage/Report/XML/Project.php]…‹:W]×h>µ¶2php-code-coverage/CodeCoverage/Report/XML/Node.php^…‹:W^¸ðN¶1php-code-coverage/CodeCoverage/Report/Factory.phpk…‹:Wkrp2”¶7php-code-coverage/CodeCoverage/Report/HTML/Renderer.php!…‹:W!Ô«u¶Jphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/css/style.css+…‹:W+Y`üg¶Rphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css9Ê…‹:W9ÊÜ›2ø¶Nphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/css/nv.d3.min.cssX%…‹:WX%0,¶Kphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/file.html.distþ +…‹:Wþ +øýŠD¶Pphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.dist…‹:Wýò{¶Pphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.distg…‹:WgV³ P¶Uphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist5…‹:W5ñZˆ]¶Sphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist1…‹:W1itLì¶Rphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.distx…‹:Wx*öŒ¶Pphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/html5shiv.min.jsL +…‹:WL +F¨¶Iphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/d3.min.jsUN…‹:WUN;Áë1¶Pphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.jsoŒ…‹:WoŒ;¦ ¶Nphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/respond.min.js…‹:Wí{¶Lphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/nv.d3.min.jsú]…‹:Wú]'Â]4¶Mphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/holder.min.js m…‹:W mJësѶMphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/jquery.min.jsÕv…‹:WÕv†e“¶Pphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/directory.html.diste…‹:WeÇ¥ô¶cphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.ttf\±…‹:W\±š<œ¶dphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.woff€[…‹:W€[ê{õ¶cphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.svg¨…‹:W¨|îÆɶephp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.woff2lF…‹:WlFvèÃa¶cphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.eotŸN…‹:WŸNXDZœ¶Aphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Directory.php| …‹:W| ¡Ü¶Aphp-code-coverage/CodeCoverage/Report/HTML/Renderer/Dashboard.phpå&…‹:Wå&«îCʶ<php-code-coverage/CodeCoverage/Report/HTML/Renderer/File.php,L…‹:W,LŽ=>¶.php-code-coverage/CodeCoverage/Report/Text.phpx!…‹:Wx!`”¹k¶0php-code-coverage/CodeCoverage/Report/Clover.phpË'…‹:WË'’déV¶.php-code-coverage/CodeCoverage/Report/HTML.php=…‹:W=É0©µ¶8php-code-coverage/CodeCoverage/Report/Node/Directory.phpS(…‹:WS(Íü¶7php-code-coverage/CodeCoverage/Report/Node/Iterator.php‚…‹:W‚M¶q¶3php-code-coverage/CodeCoverage/Report/Node/File.phpãJ…‹:WãJQ‘c¶-php-code-coverage/CodeCoverage/Report/XML.phpl…‹:WlšËs›¶.php-code-coverage/CodeCoverage/Report/Node.phpê…‹:Wê»ä2Þ¶0php-code-coverage/CodeCoverage/Report/Crap4j.php,…‹:W,G8–E¶.php-code-coverage/CodeCoverage/Driver/HHVM.php^…‹:W^Å7b§¶0php-code-coverage/CodeCoverage/Driver/PHPDBG.php …‹:W °¯9Ÿ¶0php-code-coverage/CodeCoverage/Driver/Xdebug.phpx …‹:Wx â¶=php-code-coverage/CodeCoverage/Util/InvalidArgumentHelper.php=…‹:W=Êû¶'php-code-coverage/CodeCoverage/Util.phpÍ…‹:WÍ¢K϶)php-code-coverage/CodeCoverage/Filter.php-…‹:W-+XÕ¶Gphp-code-coverage/CodeCoverage/Exception/UnintentionallyCoveredCode.phpØ…‹:WØ}¶)php-code-coverage/CodeCoverage/Driver.php»…‹:W»§0¶,php-code-coverage/CodeCoverage/Exception.php¤…‹:W¤K)§¶php-code-coverage/LICENSE…‹:WЉxZ¶sebastian-exporter/LICENSE…‹:WAªe)¶sebastian-exporter/Exporter.php["…‹:W["àTœå¶php-text-template/LICENSE …‹:W Sñ:ü¶php-text-template/Template.php” …‹:W” ¯w4¯¶php-timer/LICENSE…‹:WǨAE¶php-timer/Timer.phpE …‹:WE œá6¶php-file-iterator/LICENSE …‹:W ¶És‰¶php-file-iterator/Factory.php† …‹:W† ©y§³¶php-file-iterator/Facade.php» …‹:W» ËÏ0Ž¶php-file-iterator/Iterator.phpa…‹:Wa×äÒå¶ca.pemñ…‹:Wñ‹byë¶php-token-stream/LICENSE…‹:Wƒ-& ¶php-token-stream/Token.php:b…‹:W:bî#0!¶0php-token-stream/Token/Stream/CachingFactory.php…‹:W_¶!php-token-stream/Token/Stream.phpþ@…‹:Wþ@ÜîÜœ¶phpunit-mock-objects/LICENSE…‹:WC>©¶?phpunit-mock-objects/Framework/MockObject/Invocation/Object.php¡…‹:W¡ˆ¼†9¶?phpunit-mock-objects/Framework/MockObject/Invocation/Static.phpF…‹:WFÐKp¶Iphpunit-mock-objects/Framework/MockObject/Generator/mocked_class.tpl.dist …‹:W ¨ÑFZ¶Kphpunit-mock-objects/Framework/MockObject/Generator/proxied_method.tpl.distÌ…‹:WÌ?a§¶Hphpunit-mock-objects/Framework/MockObject/Generator/trait_class.tpl.dist7…‹:W7²[$~¶Pphpunit-mock-objects/Framework/MockObject/Generator/mocked_class_method.tpl.distí…‹:WíÚ4Þ¶Jphpunit-mock-objects/Framework/MockObject/Generator/mocked_method.tpl.dist„…‹:W„ãbVæ¶Qphpunit-mock-objects/Framework/MockObject/Generator/mocked_static_method.tpl.dist—…‹:W—+F°ƒ¶Hphpunit-mock-objects/Framework/MockObject/Generator/wsdl_method.tpl.dist<…‹:W<¾Ði‰¶Gphpunit-mock-objects/Framework/MockObject/Generator/wsdl_class.tpl.dist³…‹:W³w&S¶Kphpunit-mock-objects/Framework/MockObject/Generator/unmocked_clone.tpl.distŸ…‹:WŸ8W}ضIphpunit-mock-objects/Framework/MockObject/Generator/mocked_clone.tpl.dist„…‹:W„œaT¶Ephpunit-mock-objects/Framework/MockObject/Builder/ParametersMatch.php…‹:Wsґζ?phpunit-mock-objects/Framework/MockObject/Builder/Namespace.php²…‹:W²M쉔¶>phpunit-mock-objects/Framework/MockObject/Builder/Identity.php”…‹:W”(²4ã¶Fphpunit-mock-objects/Framework/MockObject/Builder/InvocationMocker.phpv…‹:WvÔÐÂñ¶Ephpunit-mock-objects/Framework/MockObject/Builder/MethodNameMatch.php)…‹:W)sòõ¶:phpunit-mock-objects/Framework/MockObject/Builder/Stub.phpo…‹:Wohrâ¶;phpunit-mock-objects/Framework/MockObject/Builder/Match.phpW…‹:WWEƒA¶8phpunit-mock-objects/Framework/MockObject/MockObject.php9…‹:W9¾éC…¶Dphpunit-mock-objects/Framework/MockObject/Stub/MatcherCollection.php:…‹:W:µYŠ¶Aphpunit-mock-objects/Framework/MockObject/Stub/ReturnValueMap.php|…‹:W|uB è¶Cphpunit-mock-objects/Framework/MockObject/Stub/ConsecutiveCalls.php“…‹:W“2 Æ ¶9phpunit-mock-objects/Framework/MockObject/Stub/Return.php¤…‹:W¤L¢f+¶Aphpunit-mock-objects/Framework/MockObject/Stub/ReturnCallback.php•…‹:W•g‰´`¶Aphpunit-mock-objects/Framework/MockObject/Stub/ReturnArgument.phpÿ…‹:Wÿ9)`¶=phpunit-mock-objects/Framework/MockObject/Stub/ReturnSelf.phpÂ…‹:W«¤I¶<phpunit-mock-objects/Framework/MockObject/Stub/Exception.phpÍ…‹:WÍ<÷à¶Hphpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtLeastOnce.phpÐ…‹:WÐcîþI¶Bphpunit-mock-objects/Framework/MockObject/Matcher/InvokedCount.php  …‹:W  SžG¶@phpunit-mock-objects/Framework/MockObject/Matcher/MethodName.php …‹:W v´Š¶Ephpunit-mock-objects/Framework/MockObject/Matcher/AnyInvokedCount.phpà…‹:Wà“Ds$¶Ephpunit-mock-objects/Framework/MockObject/Matcher/InvokedRecorder.php`…‹:W`!Íñè¶Dphpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtIndex.php …‹:W •åY©¶Iphpunit-mock-objects/Framework/MockObject/Matcher/StatelessInvocation.phpk…‹:Wkû•Ï¶@phpunit-mock-objects/Framework/MockObject/Matcher/Parameters.phpª…‹:WªœPjî¶@phpunit-mock-objects/Framework/MockObject/Matcher/Invocation.php…‹:WKôb¶Iphpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtLeastCount.phpü…‹:Wü R¶Kphpunit-mock-objects/Framework/MockObject/Matcher/ConsecutiveParameters.php¨…‹:W¨iOm¶Hphpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtMostCount.phpñ…‹:WñÃË÷¶Cphpunit-mock-objects/Framework/MockObject/Matcher/AnyParameters.phpC…‹:WC¹£¶9phpunit-mock-objects/Framework/MockObject/MockBuilder.php…‹:W\_¶>phpunit-mock-objects/Framework/MockObject/InvocationMocker.php…‹:WÌ2…׶7phpunit-mock-objects/Framework/MockObject/Generator.php•…‹:W•§Ü ó¶8phpunit-mock-objects/Framework/MockObject/Verifiable.php²…‹:W²”'L¶Nphpunit-mock-objects/Framework/MockObject/Exception/BadMethodCallException.phpÁ…‹:WÁ¥Ò).¶Hphpunit-mock-objects/Framework/MockObject/Exception/RuntimeException.phpµ…‹:WµY·n4¶Aphpunit-mock-objects/Framework/MockObject/Exception/Exception.php¦…‹:W¦]ÃT¶8phpunit-mock-objects/Framework/MockObject/Invocation.php…‹:W«s¶5phpunit-mock-objects/Framework/MockObject/Matcher.php¨ …‹:W¨ 7÷/¶2phpunit-mock-objects/Framework/MockObject/Stub.php\…‹:W\f+Fж7phpunit-mock-objects/Framework/MockObject/Invokable.php9…‹:W9Ùÿ~†¶. + */ + +namespace Doctrine\Instantiator; + +use Closure; +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use ReflectionClass; + +/** + * {@inheritDoc} + * + * @author Marco Pivetta + */ +final class Instantiator implements InstantiatorInterface +{ + /** + * Markers used internally by PHP to define whether {@see \unserialize} should invoke + * the method {@see \Serializable::unserialize()} when dealing with classes implementing + * the {@see \Serializable} interface. + */ + const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; + const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; + + /** + * @var \Closure[] of {@see \Closure} instances used to instantiate specific classes + */ + private static $cachedInstantiators = array(); + + /** + * @var object[] of objects that can directly be cloned + */ + private static $cachedCloneables = array(); + + /** + * {@inheritDoc} + */ + public function instantiate($className) + { + if (isset(self::$cachedCloneables[$className])) { + return clone self::$cachedCloneables[$className]; + } + + if (isset(self::$cachedInstantiators[$className])) { + $factory = self::$cachedInstantiators[$className]; + + return $factory(); + } + + return $this->buildAndCacheFromFactory($className); + } + + /** + * Builds the requested object and caches it in static properties for performance + * + * @param string $className + * + * @return object + */ + private function buildAndCacheFromFactory($className) + { + $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); + $instance = $factory(); + + if ($this->isSafeToClone(new ReflectionClass($instance))) { + self::$cachedCloneables[$className] = clone $instance; + } + + return $instance; + } + + /** + * Builds a {@see \Closure} capable of instantiating the given $className without + * invoking its constructor. + * + * @param string $className + * + * @return Closure + */ + private function buildFactory($className) + { + $reflectionClass = $this->getReflectionClass($className); + + if ($this->isInstantiableViaReflection($reflectionClass)) { + return function () use ($reflectionClass) { + return $reflectionClass->newInstanceWithoutConstructor(); + }; + } + + $serializedString = sprintf( + '%s:%d:"%s":0:{}', + $this->getSerializationFormat($reflectionClass), + strlen($className), + $className + ); + + $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); + + return function () use ($serializedString) { + return unserialize($serializedString); + }; + } + + /** + * @param string $className + * + * @return ReflectionClass + * + * @throws InvalidArgumentException + */ + private function getReflectionClass($className) + { + if (! class_exists($className)) { + throw InvalidArgumentException::fromNonExistingClass($className); + } + + $reflection = new ReflectionClass($className); + + if ($reflection->isAbstract()) { + throw InvalidArgumentException::fromAbstractClass($reflection); + } + + return $reflection; + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) + { + set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) { + $error = UnexpectedValueException::fromUncleanUnSerialization( + $reflectionClass, + $message, + $code, + $file, + $line + ); + }); + + $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); + + restore_error_handler(); + + if ($error) { + throw $error; + } + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) + { + try { + unserialize($serializedString); + } catch (Exception $exception) { + restore_error_handler(); + + throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); + } + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function isInstantiableViaReflection(ReflectionClass $reflectionClass) + { + if (\PHP_VERSION_ID >= 50600) { + return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); + } + + return \PHP_VERSION_ID >= 50400 && ! $this->hasInternalAncestors($reflectionClass); + } + + /** + * Verifies whether the given class is to be considered internal + * + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function hasInternalAncestors(ReflectionClass $reflectionClass) + { + do { + if ($reflectionClass->isInternal()) { + return true; + } + } while ($reflectionClass = $reflectionClass->getParentClass()); + + return false; + } + + /** + * Verifies if the given PHP version implements the `Serializable` interface serialization + * with an incompatible serialization format. If that's the case, use serialization marker + * "C" instead of "O". + * + * @link http://news.php.net/php.internals/74654 + * + * @param ReflectionClass $reflectionClass + * + * @return string the serialization format marker, either self::SERIALIZATION_FORMAT_USE_UNSERIALIZER + * or self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER + */ + private function getSerializationFormat(ReflectionClass $reflectionClass) + { + if ($this->isPhpVersionWithBrokenSerializationFormat() + && $reflectionClass->implementsInterface('Serializable') + ) { + return self::SERIALIZATION_FORMAT_USE_UNSERIALIZER; + } + + return self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER; + } + + /** + * Checks whether the current PHP runtime uses an incompatible serialization format + * + * @return bool + */ + private function isPhpVersionWithBrokenSerializationFormat() + { + return PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513; + } + + /** + * Checks if a class is cloneable + * + * @param ReflectionClass $reflection + * + * @return bool + */ + private function isSafeToClone(ReflectionClass $reflection) + { + if (method_exists($reflection, 'isCloneable') && ! $reflection->isCloneable()) { + return false; + } + + // not cloneable if it implements `__clone`, as we want to avoid calling it + return ! $reflection->hasMethod('__clone'); + } +} +. + */ + +namespace Doctrine\Instantiator\Exception; + +use Exception; +use ReflectionClass; +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Exception for given parameters causing invalid/unexpected state on instantiation + * + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface +{ + /** + * @param ReflectionClass $reflectionClass + * @param Exception $exception + * + * @return self + */ + public static function fromSerializationTriggeredException(ReflectionClass $reflectionClass, Exception $exception) + { + return new self( + sprintf( + 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', + $reflectionClass->getName() + ), + 0, + $exception + ); + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $errorString + * @param int $errorCode + * @param string $errorFile + * @param int $errorLine + * + * @return UnexpectedValueException + */ + public static function fromUncleanUnSerialization( + ReflectionClass $reflectionClass, + $errorString, + $errorCode, + $errorFile, + $errorLine + ) { + return new self( + sprintf( + 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' + . 'in file "%s" at line "%d"', + $reflectionClass->getName(), + $errorFile, + $errorLine + ), + 0, + new Exception($errorString, $errorCode) + ); + } +} +. + */ + +namespace Doctrine\Instantiator\Exception; + +use InvalidArgumentException as BaseInvalidArgumentException; +use ReflectionClass; + +/** + * Exception for invalid arguments provided to the instantiator + * + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface +{ + /** + * @param string $className + * + * @return self + */ + public static function fromNonExistingClass($className) + { + if (interface_exists($className)) { + return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className)); + } + + if (PHP_VERSION_ID >= 50400 && trait_exists($className)) { + return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); + } + + return new self(sprintf('The provided class "%s" does not exist', $className)); + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return self + */ + public static function fromAbstractClass(ReflectionClass $reflectionClass) + { + return new self(sprintf( + 'The provided class "%s" is abstract, and can not be instantiated', + $reflectionClass->getName() + )); + } +} +. + */ + +namespace Doctrine\Instantiator\Exception; + +/** + * Base exception marker interface for the instantiator component + * + * @author Marco Pivetta + */ +interface ExceptionInterface +{ +} +. + */ + +namespace Doctrine\Instantiator; + +/** + * Instantiator provides utility methods to build objects without invoking their constructors + * + * @author Marco Pivetta + */ +interface InstantiatorInterface +{ + /** + * @param string $className + * + * @return object + * + * @throws \Doctrine\Instantiator\Exception\ExceptionInterface + */ + public function instantiate($className); +} +Copyright (c) 2014 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This is the default implementation of the database tester. It receives its + * connection object from the constructor. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DefaultTester extends PHPUnit_Extensions_Database_AbstractTester +{ + /** + * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + protected $connection; + + /** + * Creates a new default database tester using the given connection. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + */ + public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + parent::__construct(); + + $this->connection = $connection; + } + + /** + * Returns the test database connection. + * + * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + public function getConnection() + { + return $this->connection; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This class loads a table metadata object with database metadata. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_TableMetaData extends PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData +{ + public function __construct($tableName, PHPUnit_Extensions_Database_DB_IMetaData $databaseMetaData) + { + $this->tableName = $tableName; + $this->columns = $databaseMetaData->getTableColumns($tableName); + $this->primaryKeys = $databaseMetaData->getTablePrimaryKeys($tableName); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides the functionality to represent a database table. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_Table extends PHPUnit_Extensions_Database_DataSet_AbstractTable +{ + /** + * Creates a new database table object. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData, PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection) + { + $this->setTableMetaData($tableMetaData); + + $pdoStatement = $databaseConnection->getConnection()->prepare(PHPUnit_Extensions_Database_DB_DataSet::buildTableSelect($tableMetaData, $databaseConnection)); + $pdoStatement->execute(); + $this->data = $pdoStatement->fetchAll(PDO::FETCH_ASSOC); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface for retreiving metadata from a database. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DB_IMetaData +{ + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames(); + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName); + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName); + + /** + * Returns the name of the default schema. + * + * @return string + */ + public function getSchema(); + + /** + * Returns a quoted schema object. (table name, column name, etc) + * + * @param string $object + * @return string + */ + public function quoteSchemaObject($object); + + /** + * Returns true if the rdbms allows cascading + * + * @return bool + */ + public function allowsCascading(); + + /** + * Disables primary keys if rdbms does not allow setting them otherwise + * + * @param string $tableName + */ + public function disablePrimaryKeys($tableName); + + /** + * Reenables primary keys after they have been disabled + * + * @param string $tableName + */ + public function enablePrimaryKeys($tableName); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface for communicating with a database. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DB_IDatabaseConnection +{ + /** + * Close this connection. + */ + public function close(); + + /** + * Creates a dataset containing the specified table names. If no table + * names are specified then it will created a dataset over the entire + * database. + * + * @param array $tableNames + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + public function createDataSet(Array $tableNames = NULL); + + /** + * Creates a table with the result of the specified SQL statement. + * + * @param string $resultName + * @param string $sql + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function createQueryTable($resultName, $sql); + + /** + * Returns a PDO Connection + * + * @return PDO + */ + public function getConnection(); + + /** + * Returns a database metadata object that can be used to retrieve table + * meta data from the database. + * + * @return PHPUnit_Extensions_Database_DB_IMetaData + */ + public function getMetaData(); + + /** + * Returns the number of rows in the given table. You can specify an + * optional where clause to return a subset of the table. + * + * @param string $tableName + * @param string $whereClause + * @param int + */ + public function getRowCount($tableName, $whereClause = NULL); + + /** + * Returns the schema for the connection. + * + * @return string + */ + public function getSchema(); + + /** + * Returns a quoted schema object. (table name, column name, etc) + * + * @param string $object + * @return string + */ + public function quoteSchemaObject($object); + + /** + * Returns the command used to truncate a table. + * + * @return string + */ + public function getTruncateCommand(); + + /** + * Returns true if the connection allows cascading + * + * @return bool + */ + public function allowsCascading(); + + /** + * Disables primary keys if connection does not allow setting them otherwise + * + * @param string $tableName + */ + public function disablePrimaryKeys($tableName); + + /** + * Reenables primary keys after they have been disabled + * + * @param string $tableName + */ + public function enablePrimaryKeys($tableName); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides functionality to retrieve meta data from a Microsoft SQL Server database. + * + * @since Class available since Release 1.1.0 + */ +class PHPUnit_Extensions_Database_DB_MetaData_SqlSrv extends PHPUnit_Extensions_Database_DB_MetaData +{ + /** + * No character used to quote schema objects. + * @var string + */ + protected $schemaObjectQuoteChar = ''; + + /** + * The command used to perform a TRUNCATE operation. + * @var string + */ + protected $truncateCommand = 'TRUNCATE TABLE'; + + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames() + { + $query = "SELECT name + FROM sysobjects + WHERE type='U'"; + + $statement = $this->pdo->prepare($query); + $statement->execute(); + + $tableNames = array(); + while (($tableName = $statement->fetchColumn(0))) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + $query = "SELECT c.name + FROM syscolumns c + LEFT JOIN sysobjects o ON c.id = o.id + WHERE o.name = '$tableName'"; + + $statement = $this->pdo->prepare($query); + $statement->execute(); + + $columnNames = array(); + while (($columnName = $statement->fetchColumn(0))) { + $columnNames[] = $columnName; + } + + return $columnNames; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + $query = "EXEC sp_statistics '$tableName'"; + $statement = $this->pdo->prepare($query); + $statement->execute(); + $statement->setFetchMode(PDO::FETCH_ASSOC); + + $columnNames = array(); + while (($column = $statement->fetch())) { + if ($column['TYPE'] == 1) { + $columnNames[] = $column['COLUMN_NAME']; + } + } + + return $columnNames; + } + + /** + * Allow overwriting identities for the given table. + * + * @param string $tableName + */ + public function disablePrimaryKeys($tableName) + { + try { + $query = "SET IDENTITY_INSERT $tableName ON"; + $this->pdo->exec($query); + } + catch (PDOException $e) { + // ignore the error here - can happen if primary key is not an identity + } + } + + /** + * Reenable auto creation of identities for the given table. + * + * @param string $tableName + */ + public function enablePrimaryKeys($tableName) + { + try { + $query = "SET IDENTITY_INSERT $tableName OFF"; + $this->pdo->exec($query); + } + catch (PDOException $e) { + // ignore the error here - can happen if primary key is not an identity + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides functionality to retrieve meta data from an Sqlite database. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_MetaData_Sqlite extends PHPUnit_Extensions_Database_DB_MetaData +{ + protected $columns = array(); + + protected $keys = array(); + + protected $truncateCommand = 'DELETE FROM'; + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames() + { + $query = " + SELECT name + FROM sqlite_master + WHERE + type='table' AND + name <> 'sqlite_sequence' + ORDER BY name + "; + + $result = $this->pdo->query($query); + + $tableNames = array(); + + while ($tableName = $result->fetchColumn(0)) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + if (!isset($this->columns[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->columns[$tableName]; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + if (!isset($this->keys[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->keys[$tableName]; + } + + /** + * Loads column info from a sqlite database. + * + * @param string $tableName + */ + protected function loadColumnInfo($tableName) + { + $query = "PRAGMA table_info('{$tableName}')"; + $statement = $this->pdo->query($query); + + /* @var $statement PDOStatement */ + $this->columns[$tableName] = array(); + $this->keys[$tableName] = array(); + + while ($columnData = $statement->fetch(PDO::FETCH_NUM)) { + $this->columns[$tableName][] = $columnData[1]; + + if ($columnData[5] == 1) { + $this->keys[$tableName][] = $columnData[1]; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides functionality to retrieve meta data from a PostgreSQL database. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_MetaData_PgSQL extends PHPUnit_Extensions_Database_DB_MetaData +{ + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames() + { + $query = " + SELECT DISTINCT + TABLE_NAME + FROM INFORMATION_SCHEMA.TABLES + WHERE + TABLE_TYPE='BASE TABLE' AND + TABLE_SCHEMA = ? + ORDER BY TABLE_NAME + "; + + $statement = $this->pdo->prepare($query); + $statement->execute(array($this->getSchema())); + + $tableNames = array(); + while ($tableName = $statement->fetchColumn(0)) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + if (!isset($this->columns[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->columns[$tableName]; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + if (!isset($this->keys[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->keys[$tableName]; + } + + /** + * Loads column info from a database table. + * + * @param string $tableName + */ + protected function loadColumnInfo($tableName) + { + $this->columns[$tableName] = array(); + $this->keys[$tableName] = array(); + + $columnQuery = ' + SELECT DISTINCT + COLUMN_NAME, ORDINAL_POSITION + FROM INFORMATION_SCHEMA.COLUMNS + WHERE + TABLE_NAME = ? AND + TABLE_SCHEMA = ? + ORDER BY ORDINAL_POSITION + '; + + $columnStatement = $this->pdo->prepare($columnQuery); + $columnStatement->execute(array($tableName, $this->getSchema())); + + while ($columName = $columnStatement->fetchColumn(0)) { + $this->columns[$tableName][] = $columName; + } + + $keyQuery = " + SELECT + KCU.COLUMN_NAME, + KCU.ORDINAL_POSITION + FROM + INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU + LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS as TC + ON TC.TABLE_NAME = KCU.TABLE_NAME AND + TC.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME + WHERE + TC.CONSTRAINT_TYPE = 'PRIMARY KEY' AND + TC.TABLE_NAME = ? AND + TC.TABLE_SCHEMA = ? + ORDER BY + KCU.ORDINAL_POSITION ASC + "; + + $keyStatement = $this->pdo->prepare($keyQuery); + $keyStatement->execute(array($tableName, $this->getSchema())); + + while ($columName = $keyStatement->fetchColumn(0)) { + $this->keys[$tableName][] = $columName; + } + } + + /** + * Returns the schema for the connection. + * + * @return string + */ + public function getSchema() + { + if (empty($this->schema)) { + return 'public'; + } else { + return $this->schema; + } + } + + /** + * Returns true if the rdbms allows cascading + * + * @return bool + */ + public function allowsCascading() + { + return TRUE; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides functionality to retrieve meta data from a MySQL database. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_MetaData_MySQL extends PHPUnit_Extensions_Database_DB_MetaData +{ + protected $schemaObjectQuoteChar = '`'; + + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames() + { + $query = 'SHOW TABLES'; + $statement = $this->pdo->prepare($query); + $statement->execute(); + + $tableNames = array(); + while (($tableName = $statement->fetchColumn(0))) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + $query = 'SHOW COLUMNS FROM ' . $this->quoteSchemaObject($tableName); + $statement = $this->pdo->prepare($query); + $statement->execute(); + + $columnNames = array(); + while (($columnName = $statement->fetchColumn(0))) { + $columnNames[] = $columnName; + } + + return $columnNames; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + $query = 'SHOW INDEX FROM ' . $this->quoteSchemaObject($tableName); + $statement = $this->pdo->prepare($query); + $statement->execute(); + $statement->setFetchMode(PDO::FETCH_ASSOC); + + $columnNames = array(); + while (($column = $statement->fetch())) { + if ($column['Key_name'] == 'PRIMARY') { + $columnNames[] = $column['Column_name']; + } + } + + return $columnNames; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides functionality to retrieve meta data from a Firebird database. + * + * @version Release: 1.1.2 + * @since + */ +class PHPUnit_Extensions_Database_DB_MetaData_Firebird extends PHPUnit_Extensions_Database_DB_MetaData +{ + /** + * The command used to perform a TRUNCATE operation. + * @var string + */ + protected $truncateCommand = 'DELETE FROM'; + + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames() + { + $query = " + SELECT DISTINCT + TABLE_NAME + FROM INFORMATION_SCHEMA.TABLES + WHERE + TABLE_TYPE='BASE TABLE' AND + TABLE_SCHEMA = ? + ORDER BY TABLE_NAME + "; + + $query = " + select + RDB$RELATION_NAME as TABLE_NAME + from RDB$RELATIONS + where + ((RDB$RELATION_TYPE = 0) or + (RDB$RELATION_TYPE is null)) and + (RDB$SYSTEM_FLAG = 0) + order by (RDB$RELATION_NAME) + "; + + $statement = $this->pdo->prepare($query); + $statement->execute(array($this->getSchema())); + + $tableNames = array(); + while ($tableName = $statement->fetchColumn(0)) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + if (!isset($this->columns[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->columns[$tableName]; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + if (!isset($this->keys[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->keys[$tableName]; + } + + /** + * Loads column info from a database table. + * + * @param string $tableName + */ + protected function loadColumnInfo($tableName) + { + $this->columns[$tableName] = array(); + $this->keys[$tableName] = array(); + + $columnQuery = ' + SELECT DISTINCT + COLUMN_NAME, ORDINAL_POSITION + FROM INFORMATION_SCHEMA.COLUMNS + WHERE + TABLE_NAME = ? AND + TABLE_SCHEMA = ? + ORDER BY ORDINAL_POSITION + '; + + $columnQuery = " + select + rf.RDB\$FIELD_NAME as COLUMN_NAME, + rf.RDB\$FIELD_POSITION as ORDINAL_POSITION + from RDB\$RELATION_FIELDS as rf + where + upper(RDB\$RELATION_NAME) = upper(?) + order by + ORDINAL_POSITION + + "; + + $columnStatement = $this->pdo->prepare($columnQuery); + $columnStatement->execute(array($tableName)); + + while ($columName = $columnStatement->fetchColumn(0)) { + $this->columns[$tableName][] = $columName; + } + + $keyQuery = " + SELECT + KCU.COLUMN_NAME, + KCU.ORDINAL_POSITION + FROM + INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU + LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS as TC + ON TC.TABLE_NAME = KCU.TABLE_NAME + WHERE + TC.CONSTRAINT_TYPE = 'PRIMARY KEY' AND + TC.TABLE_NAME = ? AND + TC.TABLE_SCHEMA = ? + ORDER BY + KCU.ORDINAL_POSITION ASC + "; + + $keyQuery = " + select + idseg.rdb\$field_name as COLUMN_NAME, + idseg.rdb\$field_position as ORDINAL_POSITION, + rc.rdb\$relation_name as tablename, + rc.rdb\$constraint_name as pk_name + from + RDB\$RELATION_CONSTRAINTS AS rc + left join + rdb\$index_segments as idseg on + (rc.rdb\$index_name = idseg.rdb\$index_name) + where + rc.RDB\$CONSTRAINT_TYPE = 'PRIMARY KEY' + and upper(rc.RDB\$RELATION_NAME) = upper(?) + order by + rc.rdb\$constraint_name, idseg.rdb\$field_position + "; + + $keyStatement = $this->pdo->prepare($keyQuery); + $keyStatement->execute(array($tableName)); + + while ($columName = $keyStatement->fetchColumn(0)) { + $this->keys[$tableName][] = $columName; + } + } + + /** + * Returns the schema for the connection. + * + * @return string + */ + public function getSchema() + { + if (empty($this->schema)) { + return 'public'; + } else { + return $this->schema; + } + } + + /** + * Returns true if the rdbms allows cascading + * + * @return bool + */ + public function allowsCascading() + { + return false; + } + + /** + * Returns a quoted schema object. (table name, column name, etc) + * + * @param string $object + * @return string + */ + public function quoteSchemaObject($object) { + return $object; //firebird does not allow object quoting + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides functionality to retrieve meta data from a database with information_schema support. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_MetaData_InformationSchema extends PHPUnit_Extensions_Database_DB_MetaData +{ + protected $columns = array(); + + protected $keys = array(); + + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames() + { + $query = " + SELECT DISTINCT + TABLE_NAME + FROM INFORMATION_SCHEMA.TABLES + WHERE + TABLE_TYPE='BASE TABLE' AND + TABLE_SCHEMA = ? + ORDER BY TABLE_NAME + "; + + $statement = $this->pdo->prepare($query); + $statement->execute(array($this->getSchema())); + + $tableNames = array(); + while ($tableName = $statement->fetchColumn(0)) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + if (!isset($this->columns[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->columns[$tableName]; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + if (!isset($this->keys[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->keys[$tableName]; + } + + /** + * Loads column info from a sqlite database. + * + * @param string $tableName + */ + protected function loadColumnInfo($tableName) + { + $this->columns[$tableName] = array(); + $this->keys[$tableName] = array(); + + $columnQuery = ' + SELECT DISTINCT + COLUMN_NAME + FROM INFORMATION_SCHEMA.COLUMNS + WHERE + TABLE_NAME = ? AND + TABLE_SCHEMA = ? + ORDER BY ORDINAL_POSITION + '; + + $columnStatement = $this->pdo->prepare($columnQuery); + $columnStatement->execute(array($tableName, $this->getSchema())); + + while ($columName = $columnStatement->fetchColumn(0)) { + $this->columns[$tableName][] = $columName; + } + + $keyQuery = " + SELECT + KCU.COLUMN_NAME + FROM + INFORMATION_SCHEMA.TABLE_CONSTRAINTS as TC, + INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU + WHERE + TC.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME AND + TC.TABLE_NAME = KCU.TABLE_NAME AND + TC.TABLE_SCHEMA = KCU.TABLE_SCHEMA AND + TC.CONSTRAINT_TYPE = 'PRIMARY KEY' AND + TC.TABLE_NAME = ? AND + TC.TABLE_SCHEMA = ? + ORDER BY + KCU.ORDINAL_POSITION ASC + "; + + $keyStatement = $this->pdo->prepare($keyQuery); + $keyStatement->execute(array($tableName, $this->getSchema())); + + while ($columName = $keyStatement->fetchColumn(0)) { + $this->keys[$tableName][] = $columName; + } + } +} +pdo->query($query); + + while ($tableName = $result->fetchColumn(0)) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + if (!isset($this->columns[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->columns[$tableName]; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + if (!isset($this->keys[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->keys[$tableName]; + } + + /** + * Loads column info from a sql server database. + * + * @param string $tableName + */ + protected function loadColumnInfo($tableName) + { + $query = "SELECT name + FROM sys.columns + WHERE object_id = OBJECT_ID('" . $tableName . "') + ORDER BY column_id"; + + $result = $this->pdo->query($query); + + while ($columnName = $result->fetchColumn(0)) { + $this->columns[$tableName][] = $columnName; + } + + $keyQuery = "SELECT COL_NAME(ic.OBJECT_ID,ic.column_id) AS ColumnName + FROM sys.indexes AS i INNER JOIN + sys.index_columns AS ic ON i.OBJECT_ID = ic.OBJECT_ID + AND i.index_id = ic.index_id + WHERE i.is_primary_key = 1 AND OBJECT_NAME(ic.OBJECT_ID) = '" . $tableName . "'"; + + $result = $this->pdo->query($keyQuery); + + while ($columnName = $result->fetchColumn(0)) { + $this->keys[$tableName][] = $columnName; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides functionality to retrieve meta data from an Oracle database. + * + * @since Class available since Release 3.2.3 + */ +class PHPUnit_Extensions_Database_DB_MetaData_Oci extends PHPUnit_Extensions_Database_DB_MetaData +{ + /** + * No character used to quote schema objects. + * @var string + */ + protected $schemaObjectQuoteChar = ''; + + /** + * The command used to perform a TRUNCATE operation. + * @var string + */ + protected $truncateCommand = 'TRUNCATE TABLE'; + + /** + * @var array + */ + protected $columns = array(); + + /** + * @var array + */ + protected $keys = array(); + + /** + * Returns an array containing the names of all the tables in the database. + * + * @return array + */ + public function getTableNames() + { + $tableNames = array(); + + $query = "SELECT table_name + FROM cat + WHERE table_type='TABLE' + ORDER BY table_name"; + + $result = $this->pdo->query($query); + + while ($tableName = $result->fetchColumn(0)) { + $tableNames[] = $tableName; + } + + return $tableNames; + } + + /** + * Returns an array containing the names of all the columns in the + * $tableName table, + * + * @param string $tableName + * @return array + */ + public function getTableColumns($tableName) + { + if (!isset($this->columns[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->columns[$tableName]; + } + + /** + * Returns an array containing the names of all the primary key columns in + * the $tableName table. + * + * @param string $tableName + * @return array + */ + public function getTablePrimaryKeys($tableName) + { + if (!isset($this->keys[$tableName])) { + $this->loadColumnInfo($tableName); + } + + return $this->keys[$tableName]; + } + + /** + * Loads column info from a oracle database. + * + * @param string $tableName + */ + protected function loadColumnInfo($tableName) + { + $ownerQuery = ''; + $conOwnerQuery = ''; + $tableParts = $this->splitTableName($tableName); + + $this->columns[$tableName] = array(); + $this->keys[$tableName] = array(); + + if (!empty($tableParts['schema'])) + { + $ownerQuery = " AND OWNER = '{$tableParts['schema']}'"; + $conOwnerQuery = " AND a.owner = '{$tableParts['schema']}'"; + } + + $query = "SELECT DISTINCT COLUMN_NAME + FROM USER_TAB_COLUMNS + WHERE TABLE_NAME='" . $tableParts['table'] . "' + $ownerQuery + ORDER BY COLUMN_NAME"; + + $result = $this->pdo->query($query); + + while ($columnName = $result->fetchColumn(0)) { + $this->columns[$tableName][] = $columnName; + } + + $keyQuery = "SELECT b.column_name + FROM user_constraints a, user_cons_columns b + WHERE a.constraint_type='P' + AND a.constraint_name=b.constraint_name + $conOwnerQuery + AND a.table_name = '" . $tableParts['table'] . "' "; + + $result = $this->pdo->query($keyQuery); + + while ($columnName = $result->fetchColumn(0)) { + $this->keys[$tableName][] = $columnName; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides access to a database instance as a data set. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_FilteredDataSet extends PHPUnit_Extensions_Database_DB_DataSet +{ + /** + * @var Array + */ + protected $tableNames; + + /** + * Creates a new dataset using the given database connection. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection + */ + public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection, Array $tableNames) + { + parent::__construct($databaseConnection); + $this->tableNames = $tableNames; + } + + /** + * Returns a list of table names for the database + * + * @return Array + */ + public function getTableNames() + { + return $this->tableNames; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides iterative access to tables from a database instance. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_TableIterator implements PHPUnit_Extensions_Database_DataSet_ITableIterator +{ + /** + * An array of tablenames. + * + * @var Array + */ + protected $tableNames; + + /** + * If this property is true then the tables will be iterated in reverse + * order. + * + * @var bool + */ + protected $reverse; + + /** + * The database dataset that this iterator iterates over. + * + * @var PHPUnit_Extensions_Database_DB_DataSet + */ + protected $dataSet; + + public function __construct($tableNames, PHPUnit_Extensions_Database_DB_DataSet $dataSet, $reverse = FALSE) + { + $this->tableNames = $tableNames; + $this->dataSet = $dataSet; + $this->reverse = $reverse; + + $this->rewind(); + } + + /** + * Returns the current table. + * + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function getTable() + { + return $this->current(); + } + + /** + * Returns the current table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData() + { + return $this->current()->getTableMetaData(); + } + + /** + * Returns the current table. + * + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function current() + { + $tableName = current($this->tableNames); + + return $this->dataSet->getTable($tableName); + } + + /** + * Returns the name of the current table. + * + * @return string + */ + public function key() + { + return $this->current()->getTableMetaData()->getTableName(); + } + + /** + * advances to the next element. + */ + public function next() + { + if ($this->reverse) { + prev($this->tableNames); + } else { + next($this->tableNames); + } + } + + /** + * Rewinds to the first element + */ + public function rewind() + { + if ($this->reverse) { + end($this->tableNames); + } else { + reset($this->tableNames); + } + } + + /** + * Returns true if the current index is valid + * + * @return bool + */ + public function valid() + { + return (current($this->tableNames) !== FALSE); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic constructor for all meta data classes and a factory for + * generating the appropriate meta data class. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_DB_MetaData implements PHPUnit_Extensions_Database_DB_IMetaData +{ + protected static $metaDataClassMap = array( + 'pgsql' => 'PHPUnit_Extensions_Database_DB_MetaData_PgSQL', + 'mysql' => 'PHPUnit_Extensions_Database_DB_MetaData_MySQL', + 'oci' => 'PHPUnit_Extensions_Database_DB_MetaData_Oci', + 'sqlite' => 'PHPUnit_Extensions_Database_DB_MetaData_Sqlite', + 'sqlite2' => 'PHPUnit_Extensions_Database_DB_MetaData_Sqlite', + 'sqlsrv' => 'PHPUnit_Extensions_Database_DB_MetaData_SqlSrv', + 'firebird' => 'PHPUnit_Extensions_Database_DB_MetaData_Firebird', + 'dblib' => 'PHPUnit_Extensions_Database_DB_MetaData_Dblib' + ); + + /** + * The PDO connection used to retreive database meta data + * + * @var PDO + */ + protected $pdo; + + /** + * The default schema name for the meta data object. + * + * @var string + */ + protected $schema; + + /** + * The character used to quote schema objects. + */ + protected $schemaObjectQuoteChar = '"'; + + /** + * The command used to perform a TRUNCATE operation. + */ + protected $truncateCommand = 'TRUNCATE'; + + /** + * Creates a new database meta data object using the given pdo connection + * and schema name. + * + * @param PDO $pdo + * @param string $schema + */ + public final function __construct(PDO $pdo, $schema = '') + { + $this->pdo = $pdo; + $this->schema = $schema; + } + + /** + * Creates a meta data object based on the driver of given $pdo object and + * $schema name. + * + * @param PDO $pdo + * @param string $schema + * @return PHPUnit_Extensions_Database_DB_MetaData + */ + public static function createMetaData(PDO $pdo, $schema = '') + { + $driverName = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + if (isset(self::$metaDataClassMap[$driverName])) { + $className = self::$metaDataClassMap[$driverName]; + + if ($className instanceof ReflectionClass) { + return $className->newInstance($pdo, $schema); + } else { + return self::registerClassWithDriver($className, $driverName)->newInstance($pdo, $schema); + } + } else { + throw new PHPUnit_Extensions_Database_Exception("Could not find a meta data driver for {$driverName} pdo driver."); + } + } + + /** + * Validates and registers the given $className with the given $pdoDriver. + * It should be noted that this function will not attempt to include / + * require the file. The $pdoDriver can be determined by the value of the + * PDO::ATTR_DRIVER_NAME attribute for a pdo object. + * + * A reflection of the $className is returned. + * + * @param string $className + * @param string $pdoDriver + * @return ReflectionClass + */ + public static function registerClassWithDriver($className, $pdoDriver) + { + if (!class_exists($className)) { + throw new PHPUnit_Extensions_Database_Exception("Specified class for {$pdoDriver} driver ({$className}) does not exist."); + } + + $reflection = new ReflectionClass($className); + if ($reflection->isSubclassOf('PHPUnit_Extensions_Database_DB_MetaData')) { + return self::$metaDataClassMap[$pdoDriver] = $reflection; + } else { + throw new PHPUnit_Extensions_Database_Exception("Specified class for {$pdoDriver} driver ({$className}) does not extend PHPUnit_Extensions_Database_DB_MetaData."); + } + } + + /** + * Returns the schema for the connection. + * + * @return string + */ + public function getSchema() + { + return $this->schema; + } + + /** + * Returns a quoted schema object. (table name, column name, etc) + * + * @param string $object + * @return string + */ + public function quoteSchemaObject($object) + { + $parts = explode('.', $object); + $quotedParts = array(); + + foreach ($parts as $part) { + $quotedParts[] = $this->schemaObjectQuoteChar . + str_replace($this->schemaObjectQuoteChar, $this->schemaObjectQuoteChar . $this->schemaObjectQuoteChar, $part) . + $this->schemaObjectQuoteChar; + } + + return implode('.', $quotedParts); + } + + /** + * Seperates the schema and the table from a fully qualified table name. + * + * Returns an associative array containing the 'schema' and the 'table'. + * + * @param string $fullTableName + * @return array + */ + public function splitTableName($fullTableName) + { + if (($dot = strpos($fullTableName, '.')) !== FALSE) { + return array( + 'schema' => substr($fullTableName, 0, $dot), + 'table' => substr($fullTableName, $dot + 1) + ); + } else { + return array( + 'schema' => NULL, + 'table' => $fullTableName + ); + } + } + + /** + * Returns the command for the database to truncate a table. + * + * @return string + */ + public function getTruncateCommand() + { + return $this->truncateCommand; + } + + /** + * Returns true if the rdbms allows cascading + * + * @return bool + */ + public function allowsCascading() + { + return FALSE; + } + + /** + * Disables primary keys if the rdbms does not allow setting them otherwise + * + * @param string $tableName + */ + public function disablePrimaryKeys($tableName) + { + return; + } + + /** + * Reenables primary keys after they have been disabled + * + * @param string $tableName + */ + public function enablePrimaryKeys($tableName) + { + return; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides access to a database instance as a data set. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_DataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * An array of ITable objects. + * + * @var array + */ + protected $tables = array(); + + /** + * The database connection this dataset is using. + * + * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + protected $databaseConnection; + + /** + * Creates a new dataset using the given database connection. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection + */ + public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection) + { + $this->databaseConnection = $databaseConnection; + } + + /** + * Creates the query necessary to pull all of the data from a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData + * @return unknown + */ + public static function buildTableSelect(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData, PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection = NULL) + { + if ($tableMetaData->getTableName() == '') { + $e = new Exception('Empty Table Name'); + echo $e->getTraceAsString(); + throw $e; + } + + $columns = $tableMetaData->getColumns(); + if ($databaseConnection) { + $columns = array_map(array($databaseConnection, 'quoteSchemaObject'), $columns); + } + $columnList = implode(', ', $columns); + + if ($databaseConnection) { + $tableName = $databaseConnection->quoteSchemaObject($tableMetaData->getTableName()); + } else { + $tableName = $tableMetaData->getTableName(); + } + + $primaryKeys = $tableMetaData->getPrimaryKeys(); + if ($databaseConnection) { + $primaryKeys = array_map(array($databaseConnection, 'quoteSchemaObject'), $primaryKeys); + } + if (count($primaryKeys)) { + $orderBy = 'ORDER BY ' . implode(' ASC, ', $primaryKeys) . ' ASC'; + } else { + $orderBy = ''; + } + + return "SELECT {$columnList} FROM {$tableName} {$orderBy}"; + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DB_TableIterator + */ + protected function createIterator($reverse = FALSE) + { + return new PHPUnit_Extensions_Database_DB_TableIterator($this->getTableNames(), $this, $reverse); + } + + /** + * Returns a table object for the given table. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DB_Table + */ + public function getTable($tableName) + { + if (!in_array($tableName, $this->getTableNames())) { + throw new InvalidArgumentException("$tableName is not a table in the current database."); + } + + if (empty($this->tables[$tableName])) { + $this->tables[$tableName] = new PHPUnit_Extensions_Database_DB_Table($this->getTableMetaData($tableName), $this->databaseConnection); + } + + return $this->tables[$tableName]; + } + + /** + * Returns a table meta data object for the given table. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData + */ + public function getTableMetaData($tableName) + { + return new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $this->databaseConnection->getMetaData()->getTableColumns($tableName), $this->databaseConnection->getMetaData()->getTablePrimaryKeys($tableName)); + } + + /** + * Returns a list of table names for the database + * + * @return Array + */ + public function getTableNames() + { + return $this->databaseConnection->getMetaData()->getTableNames(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides the functionality to represent a database result set as a DBUnit + * table. + * + * @deprecated The PHPUnit_Extension_Database_DataSet_QueryTable should be used instead + * @see PHPUnit_Extension_Database_DataSet_QueryTable + * @see PHPUnit_Extension_Database_DataSet_QueryDataSet + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_ResultSetTable extends PHPUnit_Extensions_Database_DataSet_AbstractTable +{ + /** + * Creates a new result set table. + * + * @param string $tableName + * @param PDOStatement $pdoStatement + */ + public function __construct($tableName, PDOStatement $pdoStatement) + { + $this->data = $pdoStatement->fetchAll(PDO::FETCH_ASSOC); + + if (count($this->data)) { + $columns = array_keys($this->data[0]); + } else { + $columns = array(); + } + + $this->setTableMetaData(new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface for communicating with a database. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection implements PHPUnit_Extensions_Database_DB_IDatabaseConnection +{ + /** + * @var PDO + */ + protected $connection; + + /** + * The metadata object used to retrieve table meta data from the database. + * + * @var PHPUnit_Extensions_Database_DB_IMetaData + */ + protected $metaData; + + /** + * Creates a new database connection + * + * @param PDO $connection + * @param string $schema - The name of the database schema you will be testing against. + */ + public function __construct(PDO $connection, $schema = '') + { + $this->connection = $connection; + $this->metaData = PHPUnit_Extensions_Database_DB_MetaData::createMetaData($connection, $schema); + $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + /** + * Close this connection. + */ + public function close() + { + unset($this->connection); + } + + /** + * Returns a database metadata object that can be used to retrieve table + * meta data from the database. + * + * @return PHPUnit_Extensions_Database_DB_IMetaData + */ + public function getMetaData() + { + return $this->metaData; + } + + /** + * Returns the schema for the connection. + * + * @return string + */ + public function getSchema() + { + return $this->getMetaData()->getSchema(); + } + + /** + * Creates a dataset containing the specified table names. If no table + * names are specified then it will created a dataset over the entire + * database. + * + * @param array $tableNames + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + * @todo Implement the filtered data set. + */ + public function createDataSet(array $tableNames = NULL) + { + if (empty($tableNames)) { + return new PHPUnit_Extensions_Database_DB_DataSet($this); + } else { + return new PHPUnit_Extensions_Database_DB_FilteredDataSet($this, $tableNames); + } + } + + /** + * Creates a table with the result of the specified SQL statement. + * + * @param string $resultName + * @param string $sql + * @return PHPUnit_Extensions_Database_DB_Table + */ + public function createQueryTable($resultName, $sql) + { + return new PHPUnit_Extensions_Database_DataSet_QueryTable($resultName, $sql, $this); + } + + /** + * Returns this connection database configuration + * + * @return PHPUnit_Extensions_Database_Database_DatabaseConfig + */ + public function getConfig() + { + } + + /** + * Returns a PDO Connection + * + * @return PDO + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Returns the number of rows in the given table. You can specify an + * optional where clause to return a subset of the table. + * + * @param string $tableName + * @param string $whereClause + * @return int + */ + public function getRowCount($tableName, $whereClause = NULL) + { + $query = 'SELECT COUNT(*) FROM ' . $this->quoteSchemaObject($tableName); + + if (isset($whereClause)) { + $query .= " WHERE {$whereClause}"; + } + + return (int) $this->connection->query($query)->fetchColumn(); + } + + /** + * Returns a quoted schema object. (table name, column name, etc) + * + * @param string $object + * @return string + */ + public function quoteSchemaObject($object) + { + return $this->getMetaData()->quoteSchemaObject($object); + } + + /** + * Returns the command used to truncate a table. + * + * @return string + */ + public function getTruncateCommand() + { + return $this->getMetaData()->getTruncateCommand(); + } + + /** + * Returns true if the connection allows cascading + * + * @return bool + */ + public function allowsCascading() + { + return $this->getMetaData()->allowsCascading(); + } + + /** + * Disables primary keys if connection does not allow setting them otherwise + * + * @param string $tableName + */ + public function disablePrimaryKeys($tableName) + { + $this->getMetaData()->disablePrimaryKeys($tableName); + } + + /** + * Reenables primary keys after they have been disabled + * + * @param string $tableName + */ + public function enablePrimaryKeys($tableName) + { + $this->getMetaData()->enablePrimaryKeys($tableName); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Can be used as a foundation for new DatabaseTesters. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_AbstractTester implements PHPUnit_Extensions_Database_ITester +{ + /** + * @var PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + protected $setUpOperation; + + /** + * @var PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + protected $tearDownOperation; + + /** + * @var PHPUnit_Extensions_Database_DataSet_IDataSet + */ + protected $dataSet; + + /** + * @var string + */ + protected $schema; + + /** + * Creates a new database tester. + */ + public function __construct() + { + $this->setUpOperation = PHPUnit_Extensions_Database_Operation_Factory::CLEAN_INSERT(); + $this->tearDownOperation = PHPUnit_Extensions_Database_Operation_Factory::NONE(); + } + + /** + * Closes the specified connection. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + */ + public function closeConnection(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + $connection->close(); + } + + /** + * Returns the test dataset. + * + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + public function getDataSet() + { + return $this->dataSet; + } + + /** + * TestCases must call this method inside setUp(). + */ + public function onSetUp() + { + $this->getSetUpOperation()->execute($this->getConnection(), $this->getDataSet()); + } + + /** + * TestCases must call this method inside tearDown(). + */ + public function onTearDown() + { + $this->getTearDownOperation()->execute($this->getConnection(), $this->getDataSet()); + } + + /** + * Sets the test dataset to use. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet + */ + public function setDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + $this->dataSet = $dataSet; + } + + /** + * Sets the schema value. + * + * @param string $schema + */ + public function setSchema($schema) + { + $this->schema = $schema; + } + + /** + * Sets the DatabaseOperation to call when starting the test. + * + * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $setUpOperation + */ + public function setSetUpOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $setUpOperation) + { + $this->setUpOperation = $setUpOperation; + } + + /** + * Sets the DatabaseOperation to call when ending the test. + * + * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $tearDownOperation + */ + public function setTearDownOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $tearDownOperation) + { + $this->tearDownOperation = $tearDownOperation; + } + + /** + * Returns the schema value + * + * @return string + */ + protected function getSchema() + { + return $this->schema; + } + + /** + * Returns the database operation that will be called when starting the test. + * + * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation + */ + protected function getSetUpOperation() + { + return $this->setUpOperation; + } + + /** + * Returns the database operation that will be called when ending the test. + * + * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation + */ + protected function getTearDownOperation() + { + return $this->tearDownOperation; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestCase extension that provides functionality for testing and asserting + * against a real database. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_TestCase extends PHPUnit_Framework_TestCase +{ + /** + * @var PHPUnit_Extensions_Database_ITester + */ + protected $databaseTester; + + /** + * Closes the specified connection. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + */ + protected function closeConnection(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + $this->getDatabaseTester()->closeConnection($connection); + } + + /** + * Returns the test database connection. + * + * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + protected abstract function getConnection(); + + /** + * Gets the IDatabaseTester for this testCase. If the IDatabaseTester is + * not set yet, this method calls newDatabaseTester() to obtain a new + * instance. + * + * @return PHPUnit_Extensions_Database_ITester + */ + protected function getDatabaseTester() + { + if (empty($this->databaseTester)) { + $this->databaseTester = $this->newDatabaseTester(); + } + + return $this->databaseTester; + } + + /** + * Returns the test dataset. + * + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + protected abstract function getDataSet(); + + /** + * Returns the database operation executed in test setup. + * + * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation + */ + protected function getSetUpOperation() + { + return PHPUnit_Extensions_Database_Operation_Factory::CLEAN_INSERT(); + } + + /** + * Returns the database operation executed in test cleanup. + * + * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation + */ + protected function getTearDownOperation() + { + return PHPUnit_Extensions_Database_Operation_Factory::NONE(); + } + + /** + * Creates a IDatabaseTester for this testCase. + * + * @return PHPUnit_Extensions_Database_ITester + */ + protected function newDatabaseTester() + { + return new PHPUnit_Extensions_Database_DefaultTester($this->getConnection()); + } + + /** + * Creates a new DefaultDatabaseConnection using the given PDO connection + * and database schema name. + * + * @param PDO $connection + * @param string $schema + * @return PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection + */ + protected function createDefaultDBConnection(PDO $connection, $schema = '') + { + return new PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection($connection, $schema); + } + + /** + * Creates a new ArrayDataSet with the given array. + * The array parameter is an associative array of tables where the key is + * the table name and the value an array of rows. Each row is an associative + * array by itself with keys representing the field names and the values the + * actual data. + * For example: + * array( + * "addressbook" => array( + * array("id" => 1, "name" => "...", "address" => "..."), + * array("id" => 2, "name" => "...", "address" => "...") + * ) + * ) + * + * @param array $data + * @return PHPUnit_Extensions_Database_DataSet_ArrayDataSet + */ + protected function createArrayDataSet(array $data) + { + return new PHPUnit_Extensions_Database_DataSet_ArrayDataSet($data); + } + + /** + * Creates a new FlatXmlDataSet with the given $xmlFile. (absolute path.) + * + * @param string $xmlFile + * @return PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet + */ + protected function createFlatXMLDataSet($xmlFile) + { + return new PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet($xmlFile); + } + + /** + * Creates a new XMLDataSet with the given $xmlFile. (absolute path.) + * + * @param string $xmlFile + * @return PHPUnit_Extensions_Database_DataSet_XmlDataSet + */ + protected function createXMLDataSet($xmlFile) + { + return new PHPUnit_Extensions_Database_DataSet_XmlDataSet($xmlFile); + } + + /** + * Create a a new MysqlXmlDataSet with the given $xmlFile. (absolute path.) + * + * @param string $xmlFile + * @return PHPUnit_Extensions_Database_DataSet_MysqlXmlDataSet + * @since Method available since Release 1.0.0 + */ + protected function createMySQLXMLDataSet($xmlFile) + { + return new PHPUnit_Extensions_Database_DataSet_MysqlXmlDataSet($xmlFile); + } + + /** + * Returns an operation factory instance that can be used to instantiate + * new operations. + * + * @return PHPUnit_Extensions_Database_Operation_Factory + */ + protected function getOperations() + { + return new PHPUnit_Extensions_Database_Operation_Factory(); + } + + /** + * Performs operation returned by getSetUpOperation(). + */ + protected function setUp() + { + parent::setUp(); + + $this->databaseTester = NULL; + + $this->getDatabaseTester()->setSetUpOperation($this->getSetUpOperation()); + $this->getDatabaseTester()->setDataSet($this->getDataSet()); + $this->getDatabaseTester()->onSetUp(); + } + + /** + * Performs operation returned by getTearDownOperation(). + */ + protected function tearDown() + { + $this->getDatabaseTester()->setTearDownOperation($this->getTearDownOperation()); + $this->getDatabaseTester()->setDataSet($this->getDataSet()); + $this->getDatabaseTester()->onTearDown(); + + /* + * Destroy the tester after the test is run to keep DB connections + * from piling up. + */ + $this->databaseTester = NULL; + } + + /** + * Asserts that two given tables are equal. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $expected + * @param PHPUnit_Extensions_Database_DataSet_ITable $actual + * @param string $message + */ + public static function assertTablesEqual(PHPUnit_Extensions_Database_DataSet_ITable $expected, PHPUnit_Extensions_Database_DataSet_ITable $actual, $message = '') + { + $constraint = new PHPUnit_Extensions_Database_Constraint_TableIsEqual($expected); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that two given datasets are equal. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $expected + * @param PHPUnit_Extensions_Database_DataSet_ITable $actual + * @param string $message + */ + public static function assertDataSetsEqual(PHPUnit_Extensions_Database_DataSet_IDataSet $expected, PHPUnit_Extensions_Database_DataSet_IDataSet $actual, $message = '') + { + $constraint = new PHPUnit_Extensions_Database_Constraint_DataSetIsEqual($expected); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Assert that a given table has a given amount of rows + * + * @param string $tableName Name of the table + * @param int $expected Expected amount of rows in the table + * @param string $message Optional message + */ + public function assertTableRowCount($tableName, $expected, $message = '') + { + $constraint = new PHPUnit_Extensions_Database_Constraint_TableRowCount($tableName, $expected); + $actual = $this->getConnection()->getRowCount($tableName); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a given table contains a given row + * + * @param array $expectedRow Row expected to find + * @param PHPUnit_Extensions_Database_DataSet_ITable $table Table to look into + * @param string $message Optional message + */ + public function assertTableContains(array $expectedRow, PHPUnit_Extensions_Database_DataSet_ITable $table, $message = '') + { + self::assertThat($table->assertContainsRow($expectedRow), self::isTrue(), $message); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides default table functionality. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_DefaultTable extends PHPUnit_Extensions_Database_DataSet_AbstractTable +{ + /** + * Creates a new table object using the given $tableMetaData + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData) + { + $this->setTableMetaData($tableMetaData); + $this->data = array(); + } + + /** + * Adds a row to the table with optional values. + * + * @param array $values + */ + public function addRow($values = array()) + { + $this->data[] = array_replace( + array_fill_keys($this->getTableMetaData()->getColumns(), NULL), + $values + ); + } + + /** + * Adds the rows in the passed table to the current table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + public function addTableRows(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + $tableColumns = $this->getTableMetaData()->getColumns(); + $rowCount = $table->getRowCount(); + + for ($i = 0; $i < $rowCount; $i++) { + $newRow = array(); + foreach ($tableColumns as $columnName) { + $newRow[$columnName] = $table->getValue($i, $columnName); + } + $this->addRow($newRow); + } + } + + /** + * Sets the specified column of the specied row to the specified value. + * + * @param int $row + * @param string $column + * @param mixed $value + */ + public function setValue($row, $column, $value) + { + if (isset($this->data[$row])) { + $this->data[$row][$column] = $value; + } else { + throw new InvalidArgumentException('The row given does not exist.'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A dataset decorator that allows filtering out tables and table columns from + * results. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_DataSetFilter extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * The dataset being decorated. + * @var PHPUnit_Extensions_Database_DataSet_IDataSet + */ + protected $originalDataSet; + + /** + * The tables to exclude from the data set. + * @var Array + */ + protected $excludeTables = array(); + + /** + * The tables to exclude from the data set. + * @var Array + */ + protected $includeTables = array(); + + /** + * The columns to exclude from the data set. + * @var Array + */ + protected $excludeColumns = array(); + + /** + * The columns to exclude from the data set. + * @var Array + */ + protected $includeColumns = array(); + + /** + * Creates a new filtered data set. + * + * The $exclude tables should be an associative array using table names as + * the key and an array of column names to exclude for the value. If you + * would like to exclude a full table set the value of the table's entry + * to the special string '*'. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $originalDataSet + * @param Array $excludeTables @deprecated use set* methods instead. + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_IDataSet $originalDataSet, array $excludeTables = array()) + { + $this->originalDataSet = $originalDataSet; + + $tables = array(); + foreach ($excludeTables as $tableName => $values) { + if (is_array($values)) { + $this->setExcludeColumnsForTable($tableName, $values); + } elseif ($values == '*') { + $tables[] = $tableName; + } else { + $this->setExcludeColumnsForTable($tableName, (array) $values); + } + } + + $this->addExcludeTables($tables); + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected function createIterator($reverse = FALSE) + { + $original_tables = $this->originalDataSet->getIterator($reverse); + $new_tables = array(); + + foreach ($original_tables as $table) { + /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */ + $tableName = $table->getTableMetaData()->getTableName(); + + if ((!in_array($tableName, $this->includeTables) && !empty($this->includeTables)) || + in_array($tableName, $this->excludeTables) + ) { + continue; + } elseif (!empty($this->excludeColumns[$tableName]) || !empty($this->includeColumns[$tableName])) { + $new_table = new PHPUnit_Extensions_Database_DataSet_TableFilter($table); + + if (!empty($this->includeColumns[$tableName])) { + $new_table->addIncludeColumns($this->includeColumns[$tableName]); + } + + if (!empty($this->excludeColumns[$tableName])) { + $new_table->addExcludeColumns($this->excludeColumns[$tableName]); + } + + $new_tables[] = $new_table; + } else { + $new_tables[] = $table; + } + } + + return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($new_tables); + } + + /** + * Adds tables to be included in the data set. + * @param array $tables + */ + public function addIncludeTables(Array $tables) + { + $this->includeTables = array_unique(array_merge($this->includeTables, $tables)); + } + + /** + * Adds tables to be included in the data set. + * @param array $tables + */ + public function addExcludeTables(Array $tables) + { + $this->excludeTables = array_unique(array_merge($this->excludeTables, $tables)); + } + + /** + * Adds columns to include in the data set for the given table. + * @param string $table + * @param Array $columns + */ + public function setIncludeColumnsForTable($table, Array $columns) + { + $this->includeColumns[$table] = $columns; + } + + /** + * Adds columns to include in the data set for the given table. + * @param string $table + * @param Array $columns + */ + public function setExcludeColumnsForTable($table, Array $columns) + { + $this->excludeColumns[$table] = $columns; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface for creating and reading data from data sets. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DataSet_ITable +{ + /** + * Returns the table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData(); + + /** + * Returns the number of rows in this table. + * + * @return int + */ + public function getRowCount(); + + /** + * Returns the value for the given column on the given row. + * + * @param int $row + * @param int $column + */ + public function getValue($row, $column); + + /** + * Returns the an associative array keyed by columns for the given row. + * + * @param int $row + * @return array + */ + public function getRow($row); + + /** + * Asserts that the given table matches this table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default implementation of a data set. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet +{ + protected function getTableInfo(Array &$tableColumns, Array &$tableValues) + { + if ($this->xmlFileContents->getName() != 'dataset') { + throw new PHPUnit_Extensions_Database_Exception('The root element of a flat xml data set file must be called '); + } + + foreach ($this->xmlFileContents->children() as $row) { + $tableName = $row->getName(); + + if (!isset($tableColumns[$tableName])) { + $tableColumns[$tableName] = array(); + $tableValues[$tableName] = array(); + } + + $values = array(); + foreach ($row->attributes() as $name => $value) { + if (!in_array($name, $tableColumns[$tableName])) { + $tableColumns[$tableName][] = $name; + } + + $values[$name] = $value; + } + + if (count($values)) { + $tableValues[$tableName][] = $values; + } + } + } + + public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename) + { + $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_FlatXml(); + $pers->setFileName($filename); + + try { + $pers->write($dataset); + } catch (RuntimeException $e) { + throw new PHPUnit_Framework_Exception(__METHOD__ . ' called with an unwritable file.'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Allows for replacing arbitrary values or portions of values with new data. + * + * A usage for this is replacing all values == '[NULL'] with a true NULL value + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_ReplacementDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * @var PHPUnit_Extensions_Database_DataSet_IDataSet + */ + protected $dataSet; + + /** + * @var array + */ + protected $fullReplacements; + + /** + * @var array + */ + protected $subStrReplacements; + + /** + * Creates a new replacement dataset + * + * You can pass in any data set that implements PHPUnit_Extensions_Database_DataSet_IDataSet + * + * @param string $delimiter + * @param string $enclosure + * @param string $escape + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet, Array $fullReplacements = array(), Array $subStrReplacements = array()) + { + $this->dataSet = $dataSet; + $this->fullReplacements = $fullReplacements; + $this->subStrReplacements = $subStrReplacements; + } + + /** + * Adds a new full replacement + * + * Full replacements will only replace values if the FULL value is a match + * + * @param string $value + * @param string $replacement + */ + public function addFullReplacement($value, $replacement) + { + $this->fullReplacements[$value] = $replacement; + } + + /** + * Adds a new substr replacement + * + * Substr replacements will replace all occurances of the substr in every column + * + * @param string $value + * @param string $replacement + */ + public function addSubStrReplacement($value, $replacement) + { + $this->subStrReplacements[$value] = $replacement; + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected function createIterator($reverse = FALSE) + { + $innerIterator = $reverse ? $this->dataSet->getReverseIterator() : $this->dataSet->getIterator(); + + return new PHPUnit_Extensions_Database_DataSet_ReplacementTableIterator($innerIterator, $this->fullReplacements, $this->subStrReplacements); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default table iterator + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_DefaultTableIterator implements PHPUnit_Extensions_Database_DataSet_ITableIterator +{ + /** + * An array of tables in the iterator. + * + * @var Array + */ + protected $tables; + + /** + * If this property is true then the tables will be iterated in reverse + * order. + * + * @var bool + */ + protected $reverse; + + /** + * Creates a new default table iterator object. + * + * @param array $tables + * @param bool $reverse + */ + public function __construct(Array $tables, $reverse = FALSE) + { + $this->tables = $tables; + $this->reverse = $reverse; + + $this->rewind(); + } + + /** + * Returns the current table. + * + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function getTable() + { + return $this->current(); + } + + /** + * Returns the current table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData() + { + return $this->current()->getTableMetaData(); + } + + /** + * Returns the current table. + * + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function current() + { + return current($this->tables); + } + + /** + * Returns the name of the current table. + * + * @return string + */ + public function key() + { + return $this->current()->getTableMetaData()->getTableName(); + } + + /** + * advances to the next element. + */ + public function next() + { + if ($this->reverse) { + prev($this->tables); + } else { + next($this->tables); + } + } + + /** + * Rewinds to the first element + */ + public function rewind() + { + if ($this->reverse) { + end($this->tables); + } else { + reset($this->tables); + } + } + + /** + * Returns true if the current index is valid + * + * @return bool + */ + public function valid() + { + return ($this->current() !== FALSE); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A table decorator that allows filtering out table columns from results. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_TableFilter extends PHPUnit_Extensions_Database_DataSet_AbstractTable +{ + /** + * The table meta data being decorated. + * @var PHPUnit_Extensions_Database_DataSet_ITable + */ + protected $originalTable; + + /** + * Creates a new table filter using the original table + * + * @param $originalTable PHPUnit_Extensions_Database_DataSet_ITable + * @param $excludeColumns Array @deprecated, use the set* methods instead. + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_ITable $originalTable, Array $excludeColumns = array()) + { + $this->originalTable = $originalTable; + $this->setTableMetaData(new PHPUnit_Extensions_Database_DataSet_TableMetaDataFilter($originalTable->getTableMetaData())); + $this->addExcludeColumns($excludeColumns); + } + + /** + * Returns the number of rows in this table. + * + * @return int + */ + public function getRowCount() + { + return $this->originalTable->getRowCount(); + } + + /** + * Returns the value for the given column on the given row. + * + * @param int $row + * @param int $column + */ + public function getValue($row, $column) + { + if (in_array($column, $this->getTableMetaData()->getColumns())) { + return $this->originalTable->getValue($row, $column); + } else { + throw new InvalidArgumentException("The given row ({$row}) and column ({$column}) do not exist in table {$this->getTableMetaData()->getTableName()}"); + } + } + + /** + * Sets the columns to include in the table. + * @param Array $includeColumns + */ + public function addIncludeColumns(Array $includeColumns) + { + $this->tableMetaData->addIncludeColumns($includeColumns); + } + + /** + * Clears the included columns. + */ + public function clearIncludeColumns() + { + $this->tableMetaData->clearIncludeColumns(); + } + + /** + * Sets the columns to exclude from the table. + * @param Array $excludeColumns + */ + public function addExcludeColumns(Array $excludeColumns) + { + $this->tableMetaData->addExcludeColumns($excludeColumns); + } + + /** + * Clears the included columns. + */ + public function clearExcludeColumns() + { + $this->tableMetaData->clearExcludeColumns(); + } + + /** + * Checks if a given row is in the table + * + * @param array $row + * + * @return bool + */ + public function assertContainsRow(Array $row) + { + $this->loadData(); + + return parent::assertContainsRow($row); + } + + /** + * Loads data into local data table if it's not already loaded + */ + protected function loadData() + { + if ($this->data === NULL) { + $data = array(); + for($row = 0;$row < $this->originalTable->getRowCount();$row++) { + $tRow = array(); + foreach($this->getTableMetaData()->getColumns() as $col) { + $tRow[$col] = $this->getValue($row, $col); + } + $data[$row] = $tRow; + } + $this->data = $data; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A MySQL XML dataset persistor. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Persistors_MysqlXml extends PHPUnit_Extensions_Database_DataSet_Persistors_Abstract +{ + /** + * @var string + */ + protected $filename; + + /** + * @var string + */ + protected $database; + + /** + * @var resource + */ + protected $fh; + + /** + * Sets the filename that this persistor will save to. + * + * @param string $filename + */ + public function setFileName($filename) + { + $this->filename = $filename; + } + + /** + * Sets the name of the database. + * + * @param string $database + */ + public function setDatabase($database) + { + $this->database = $database; + } + + /** + * Override to save the start of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + $this->fh = fopen($this->filename, 'w'); + + if ($this->fh === FALSE) { + throw new PHPUnit_Framework_Exception( + "Could not open {$this->filename} for writing see " . __CLASS__ . '::setFileName()' + ); + } + + fwrite($this->fh, '' . "\n"); + fwrite($this->fh, '' . "\n"); + fwrite($this->fh, '' . "\n"); + } + + /** + * Override to save the end of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + fwrite($this->fh, '' . "\n"); + fwrite($this->fh, '' . "\n"); + } + + /** + * Override to save the start of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + fwrite($this->fh, "\t" . '' . "\n"); + } + + /** + * Override to save the end of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + fwrite($this->fh, "\t" . '' . "\n"); + } + + /** + * Override to save a table row. + * + * @param array $row + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table) + { + fwrite($this->fh, "\t" . '' . "\n"); + + foreach ($table->getTableMetaData()->getColumns() as $columnName) { + fwrite($this->fh, "\t\t" . 'fh, '>' . htmlspecialchars($row[$columnName]) . '' . "\n"); + } else { + fwrite($this->fh, ' xsi:nil="true" />' . "\n"); + } + } + + fwrite($this->fh, "\t" . '' . "\n"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates the appropriate Persistor based on a given type and spec. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Persistors_Factory +{ + /** + * Returns the persistor. + * + * @param string $type + * @param string $spec + * @return PHPUnit_Extensions_Database_DataSet_IPersistable + */ + public function getPersistorBySpec($type, $spec) + { + switch (strtolower($type)) { + case 'xml': + $xmlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_Xml(); + $xmlPersistor->setFileName($spec); + + return $xmlPersistor; + + case 'flatxml': + $flatXmlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_FlatXml(); + $flatXmlPersistor->setFileName($spec); + + return $flatXmlPersistor; + + case 'yaml': + $yamlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_Yaml(); + $yamlPersistor->setFileName($spec); + + return $yamlPersistor; + + case 'mysqlxml': + $mysqlXmlPersistor = new PHPUnit_Extensions_Database_DataSet_Persistors_MysqlXml(); + $mysqlXmlPersistor->setFileName($spec); + + return $mysqlXmlPersistor; + + default: + throw new PHPUnit_Extensions_Database_Exception("I don't know what you want from me. PERSISTOR"); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An abstract implementation of a dataset persistor. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_DataSet_Persistors_Abstract implements PHPUnit_Extensions_Database_DataSet_IPersistable +{ + public function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + $this->saveDataSet($dataset); + } + + /** + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + protected function saveDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + $this->startDataSet($dataset); + + foreach ($dataset as $table) { + $this->saveTable($table); + } + + $this->endDataSet($dataset); + } + + /** + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function saveTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + $rowCount = $table->getRowCount(); + $this->startTable($table); + + for ($i = 0; $i < $rowCount; $i++) { + $this->row($table->getRow($i), $table); + } + + $this->endTable($table); + } + + /** + * Override to save the start of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + abstract protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset); + + /** + * Override to save the end of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + abstract protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset); + + /** + * Override to save the start of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + abstract protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table); + + /** + * Override to save the end of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + abstract protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table); + + /** + * Override to save a table row. + * + * @param array $row + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + abstract protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A yaml dataset persistor + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Persistors_Yaml implements PHPUnit_Extensions_Database_DataSet_IPersistable +{ + /** + * @var string + */ + protected $filename; + + /** + * Sets the filename that this persistor will save to. + * + * @param string $filename + */ + public function setFileName($filename) + { + $this->filename = $filename; + } + + /** + * Writes the dataset to a yaml file + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + public function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + $phpArr = array(); + $emptyTables = array(); + + foreach ($dataset as $table) { + $tableName = $table->getTableMetaData()->getTableName(); + $rowCount = $table->getRowCount(); + + if (!$rowCount) { + $emptyTables[] = $tableName; + continue; + } + + $phpArr[$tableName] = array(); + + for ($i = 0; $i < $rowCount; $i++) { + $phpArr[$tableName][] = $table->getRow($i); + } + } + + $emptyTablesAsString = ''; + + if (count($emptyTables)) { + $emptyTablesAsString = implode(":\n", $emptyTables) . ":\n\n"; + } + + file_put_contents( + $this->filename, + Symfony\Component\Yaml\Yaml::dump($phpArr, 3) . $emptyTablesAsString + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Flat XML dataset persistor. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Persistors_FlatXml extends PHPUnit_Extensions_Database_DataSet_Persistors_Abstract +{ + /** + * @var string + */ + protected $filename; + + /** + * @var resource + */ + protected $fh; + + /** + * Sets the filename that this persistor will save to. + * + * @param string $filename + */ + public function setFileName($filename) + { + $this->filename = $filename; + } + + /** + * Override to save the start of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + $this->fh = fopen($this->filename, 'w'); + + if ($this->fh === FALSE) { + throw new PHPUnit_Framework_Exception( + "Could not open {$this->filename} for writing see " . __CLASS__ . '::setFileName()' + ); + } + + fwrite($this->fh, "\n"); + fwrite($this->fh, "\n"); + } + + /** + * Override to save the end of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + fwrite($this->fh, "\n"); + } + + /** + * Override to save the start of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + if ($table->getRowCount() == 0) { + fwrite($this->fh, "\t<{$table->getTableMetaData()->getTableName()} />\n"); + } + } + + /** + * Override to save the end of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + //do nothing + } + + /** + * Override to save a table row. + * + * @param array $row + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table) + { + fwrite($this->fh, "\t<{$table->getTableMetaData()->getTableName()}\n"); + + foreach ($table->getTableMetaData()->getColumns() as $columnName) { + if (isset($row[$columnName])) { + fwrite($this->fh, "\t\t{$columnName}=\"" . htmlspecialchars($row[$columnName]) . "\"\n"); + } + } + + fwrite($this->fh, "\t/>\n"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A XML dataset persistor. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Persistors_Xml extends PHPUnit_Extensions_Database_DataSet_Persistors_Abstract +{ + /** + * @var string + */ + protected $filename; + + /** + * @var resource + */ + protected $fh; + + /** + * Sets the filename that this persistor will save to. + * + * @param string $filename + */ + public function setFileName($filename) + { + $this->filename = $filename; + } + + /** + * Override to save the start of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + protected function startDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + $this->fh = fopen($this->filename, 'w'); + + if ($this->fh === FALSE) { + throw new PHPUnit_Framework_Exception( + "Could not open {$this->filename} for writing see " . __CLASS__ . '::setFileName()' + ); + } + + fwrite($this->fh, "\n"); + fwrite($this->fh, "\n"); + } + + /** + * Override to save the end of a dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + protected function endDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) + { + fwrite($this->fh, "\n"); + } + + /** + * Override to save the start of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function startTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + fwrite($this->fh, "\tgetTableMetaData()->getTableName()}\">\n"); + + foreach ($table->getTableMetaData()->getColumns() as $columnName) { + fwrite($this->fh, "\t\t{$columnName}\n"); + } + } + + /** + * Override to save the end of a table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function endTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + fwrite($this->fh, "\t
\n"); + } + + /** + * Override to save a table row. + * + * @param array $row + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + protected function row(Array $row, PHPUnit_Extensions_Database_DataSet_ITable $table) + { + fwrite($this->fh, "\t\t\n"); + + foreach ($table->getTableMetaData()->getColumns() as $columnName) { + if (isset($row[$columnName])) { + fwrite($this->fh, "\t\t\t" . htmlspecialchars($row[$columnName]) . "\n"); + } else { + fwrite($this->fh, "\t\t\t\n"); + } + } + + fwrite($this->fh, "\t\t\n"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An interface for persisting datasets + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DataSet_IPersistable +{ + /** + * Writes the given dataset + * + * The previous dataset will be overwritten. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + */ + public function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default implementation of a data set. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_XmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet +{ + protected function getTableInfo(Array &$tableColumns, Array &$tableValues) + { + if ($this->xmlFileContents->getName() != 'dataset') { + throw new PHPUnit_Extensions_Database_Exception('The root element of an xml data set file must be called '); + } + + foreach ($this->xmlFileContents->xpath('/dataset/table') as $tableElement) { + if (empty($tableElement['name'])) { + throw new PHPUnit_Extensions_Database_Exception('Table elements must include a name attribute specifying the table name.'); + } + + $tableName = (string) $tableElement['name']; + + if (!isset($tableColumns[$tableName])) { + $tableColumns[$tableName] = array(); + } + + if (!isset($tableValues[$tableName])) { + $tableValues[$tableName] = array(); + } + + $tableInstanceColumns = array(); + + foreach ($tableElement->xpath('./column') as $columnElement) { + $columnName = (string) $columnElement; + if (empty($columnName)) { + throw new PHPUnit_Extensions_Database_Exception("Missing elements for table $tableName. Add one or more elements to the element."); + } + + if (!in_array($columnName, $tableColumns[$tableName])) { + $tableColumns[$tableName][] = $columnName; + } + + $tableInstanceColumns[] = $columnName; + } + + foreach ($tableElement->xpath('./row') as $rowElement) { + $rowValues = array(); + $index = 0; + $numOfTableInstanceColumns = count($tableInstanceColumns); + + foreach ($rowElement->children() as $columnValue) { + + if ($index >= $numOfTableInstanceColumns) { + throw new PHPUnit_Extensions_Database_Exception("Row contains more values than the number of columns defined for table $tableName."); + } + switch ($columnValue->getName()) { + case 'value': + $rowValues[$tableInstanceColumns[$index]] = (string) $columnValue; + $index++; + break; + case 'null': + $rowValues[$tableInstanceColumns[$index]] = NULL; + $index++; + break; + default: + throw new PHPUnit_Extensions_Database_Exception('Unknown element ' . $columnValue->getName() . ' in a row element.'); + } + } + + $tableValues[$tableName][] = $rowValues; + } + } + } + + public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename) + { + $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_Xml(); + $pers->setFileName($filename); + + try { + $pers->write($dataset); + } + + catch (RuntimeException $e) { + throw new PHPUnit_Framework_Exception(__METHOD__ . ' called with an unwritable file.'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Data set implementation for the output of mysqldump --xml. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_MysqlXmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet +{ + protected function getTableInfo(array &$tableColumns, array &$tableValues) + { + if ($this->xmlFileContents->getName() != 'mysqldump') { + throw new PHPUnit_Extensions_Database_Exception('The root element of a MySQL XML data set file must be called '); + } + + foreach ($this->xmlFileContents->xpath('./database/table_data') as $tableElement) { + if (empty($tableElement['name'])) { + throw new PHPUnit_Extensions_Database_Exception(' elements must include a name attribute'); + } + + $tableName = (string) $tableElement['name']; + + if (!isset($tableColumns[$tableName])) { + $tableColumns[$tableName] = array(); + } + + if (!isset($tableValues[$tableName])) { + $tableValues[$tableName] = array(); + } + + foreach ($tableElement->xpath('./row') as $rowElement) { + $rowValues = array(); + + foreach ($rowElement->xpath('./field') as $columnElement) { + if (empty($columnElement['name'])) { + throw new PHPUnit_Extensions_Database_Exception(' element name attributes cannot be empty'); + } + + $columnName = (string) $columnElement['name']; + + if (!in_array($columnName, $tableColumns[$tableName])) { + $tableColumns[$tableName][] = $columnName; + } + } + + foreach ($tableColumns[$tableName] as $columnName) { + $fields = $rowElement->xpath('./field[@name="' . $columnName . '"]'); + $column = $fields[0]; + $attr = $column->attributes('http://www.w3.org/2001/XMLSchema-instance'); + + if (isset($attr['type']) && (string) $attr['type'] === 'xs:hexBinary') { + $columnValue = pack('H*',(string) $column); + } else { + $null = isset($column['nil']) || isset($attr[0]); + $columnValue = $null ? NULL : (string) $column; + } + + $rowValues[$columnName] = $columnValue; + + } + + $tableValues[$tableName][] = $rowValues; + } + } + + foreach ($this->xmlFileContents->xpath('./database/table_structure') as $tableElement) { + if (empty($tableElement['name'])) { + throw new PHPUnit_Extensions_Database_Exception(' elements must include a name attribute'); + } + + $tableName = (string) $tableElement['name']; + + foreach ($tableElement->xpath('./field') as $fieldElement) { + if (empty($fieldElement['Field'])) { + throw new PHPUnit_Extensions_Database_Exception(' elements must include a Field attribute'); + } + + $columnName = (string) $fieldElement['Field']; + + if (!in_array($columnName, $tableColumns[$tableName])) { + $tableColumns[$tableName][] = $columnName; + } + } + } + } + + public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename) + { + $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_MysqlXml; + $pers->setFileName($filename); + + try { + $pers->write($dataset); + } + + catch (RuntimeException $e) { + throw new PHPUnit_Framework_Exception(__METHOD__ . ' called with an unwritable file.'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Allows for replacing arbitrary strings in your data sets with other values. + * + * @since Class available since Release 1.0.0 + * @todo When setTableMetaData() is taken out of the AbstractTable this class should extend AbstractTable. + */ +class PHPUnit_Extensions_Database_DataSet_ReplacementTable implements PHPUnit_Extensions_Database_DataSet_ITable +{ + /** + * @var PHPUnit_Extensions_Database_DataSet_ITable + */ + protected $table; + + /** + * @var array + */ + protected $fullReplacements; + + /** + * @var array + */ + protected $subStrReplacements; + + /** + * Creates a new replacement table + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + * @param array $fullReplacements + * @param array $subStrReplacements + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_ITable $table, Array $fullReplacements = array(), Array $subStrReplacements = array()) + { + $this->table = $table; + $this->fullReplacements = $fullReplacements; + $this->subStrReplacements = $subStrReplacements; + } + + /** + * Adds a new full replacement + * + * Full replacements will only replace values if the FULL value is a match + * + * @param string $value + * @param string $replacement + */ + public function addFullReplacement($value, $replacement) + { + $this->fullReplacements[$value] = $replacement; + } + + /** + * Adds a new substr replacement + * + * Substr replacements will replace all occurances of the substr in every column + * + * @param string $value + * @param string $replacement + */ + public function addSubStrReplacement($value, $replacement) + { + $this->subStrReplacements[$value] = $replacement; + } + + /** + * Returns the table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData() + { + return $this->table->getTableMetaData(); + } + + /** + * Returns the number of rows in this table. + * + * @return int + */ + public function getRowCount() + { + return $this->table->getRowCount(); + } + + /** + * Returns the value for the given column on the given row. + * + * @param int $row + * @param int $column + */ + public function getValue($row, $column) + { + return $this->getReplacedValue($this->table->getValue($row, $column)); + } + + /** + * Returns the an associative array keyed by columns for the given row. + * + * @param int $row + * @return array + */ + public function getRow($row) + { + $row = $this->table->getRow($row); + + return array_map(array($this, 'getReplacedValue'), $row); + } + + /** + * Asserts that the given table matches this table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other) + { + $thisMetaData = $this->getTableMetaData(); + $otherMetaData = $other->getTableMetaData(); + + if (!$thisMetaData->matches($otherMetaData) || + $this->getRowCount() != $other->getRowCount()) { + return FALSE; + } + + $columns = $thisMetaData->getColumns(); + $rowCount = $this->getRowCount(); + + for ($i = 0; $i < $rowCount; $i++) { + foreach ($columns as $columnName) { + $thisValue = $this->getValue($i, $columnName); + $otherValue = $other->getValue($i, $columnName); + if (is_numeric($thisValue) && is_numeric($otherValue)) { + if ($thisValue != $otherValue) { + return FALSE; + } + } elseif ($thisValue !== $otherValue) { + return FALSE; + } + } + } + + return TRUE; + } + + public function __toString() + { + $columns = $this->getTableMetaData()->getColumns(); + + $lineSeperator = str_repeat('+----------------------', count($columns)) . "+\n"; + $lineLength = strlen($lineSeperator) - 1; + + $tableString = $lineSeperator; + $tableString .= '| ' . str_pad($this->getTableMetaData()->getTableName(), $lineLength - 4, ' ', STR_PAD_RIGHT) . " |\n"; + $tableString .= $lineSeperator; + $tableString .= $this->rowToString($columns); + $tableString .= $lineSeperator; + + $rowCount = $this->getRowCount(); + + for ($i = 0; $i < $rowCount; $i++) { + $values = array(); + + foreach ($columns as $columnName) { + $values[] = $this->getValue($i, $columnName); + } + + $tableString .= $this->rowToString($values); + $tableString .= $lineSeperator; + } + + return "\n" . $tableString . "\n"; + } + + protected function rowToString(Array $row) + { + $rowString = ''; + + foreach ($row as $value) { + if (is_null($value)) { + $value = 'NULL'; + } + + $rowString .= '| ' . str_pad(substr($value, 0, 20), 20, ' ', STR_PAD_BOTH) . ' '; + } + + return $rowString . "|\n"; + } + + protected function getReplacedValue($value) + { + if (is_scalar($value) && array_key_exists((string) $value, $this->fullReplacements)) { + return $this->fullReplacements[$value]; + } + + else if (count($this->subStrReplacements) && isset($value)) { + return str_replace(array_keys($this->subStrReplacements), array_values($this->subStrReplacements), $value); + } + + else { + return $value; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default implementation of table meta data + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData extends PHPUnit_Extensions_Database_DataSet_AbstractTableMetaData +{ + /** + * Creates a new default table meta data object. + * + * @param string $tableName + * @param array $columns + * @param array $primaryKeys + */ + public function __construct($tableName, Array $columns, Array $primaryKeys = array()) + { + $this->tableName = $tableName; + $this->columns = $columns; + $this->primaryKeys = array(); + + foreach ($primaryKeys as $columnName) { + if (!in_array($columnName, $this->columns)) { + throw new InvalidArgumentException('Primary key column passed that is not in the column list.'); + } else { + $this->primaryKeys[] = $columnName; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates Composite Datasets + * + * Allows for creating datasets from multiple sources (csv, query, xml, etc.) + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_CompositeDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + protected $motherDataSet; + + /** + * Creates a new Composite dataset + * + * You can pass in any data set that implements PHPUnit_Extensions_Database_DataSet_IDataSet + * + * @param string $delimiter + * @param string $enclosure + * @param string $escape + */ + public function __construct(Array $dataSets = array()) + { + $this->motherDataset = new PHPUnit_Extensions_Database_DataSet_DefaultDataSet(); + + foreach ($dataSets as $dataSet) + { + $this->addDataSet($dataSet); + } + } + + /** + * Adds a new data set to the composite. + * + * The dataset may not define tables that already exist in the composite. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet + */ + public function addDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + foreach ($dataSet->getTableNames() as $tableName) + { + if (!in_array($tableName, $this->getTableNames())) { + $this->motherDataset->addTable($dataSet->getTable($tableName)); + } else { + $other = $dataSet->getTable($tableName); + $table = $this->getTable($tableName); + + if (!$table->getTableMetaData()->matches($other->getTableMetaData())) + { + throw new InvalidArgumentException("There is already a table named $tableName with different table definition"); + } + + $table->addTableRows($dataSet->getTable($tableName)); + } + } + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected function createIterator($reverse = FALSE) + { + if ($reverse) { + return $this->motherDataset->getReverseIterator(); + } else { + return $this->motherDataset->getIterator(); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides access to a database instance as a data set. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_QueryDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * An array of ITable objects. + * + * @var array + */ + protected $tables = array(); + + /** + * The database connection this dataset is using. + * + * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + protected $databaseConnection; + + /** + * Creates a new dataset using the given database connection. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection + */ + public function __construct(PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection) + { + $this->databaseConnection = $databaseConnection; + } + + public function addTable($tableName, $query = NULL) + { + if ($query === NULL) { + $query = 'SELECT * FROM ' . $tableName; + } + + $this->tables[$tableName] = new PHPUnit_Extensions_Database_DataSet_QueryTable($tableName, $query, $this->databaseConnection); + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DB_TableIterator + */ + protected function createIterator($reverse = FALSE) + { + return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse); + } + + /** + * Returns a table object for the given table. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DB_Table + */ + public function getTable($tableName) + { + if (!isset($this->tables[$tableName])) { + throw new InvalidArgumentException("$tableName is not a table in the current database."); + } + + return $this->tables[$tableName]; + } + + /** + * Returns a list of table names for the database + * + * @return Array + */ + public function getTableNames() + { + return array_keys($this->tables); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides an interface for creating data sets from data set spec strings. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DataSet_ISpec +{ + /** + * Creates a data set from a data set spec string. + * + * @param string $dataSetSpec + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + public function getDataSet($dataSetSpec); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic functionality for dbunit tables + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_AbstractTable implements PHPUnit_Extensions_Database_DataSet_ITable +{ + /** + * @var PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + protected $tableMetaData; + + /** + * A 2-dimensional array containing the data for this table. + * + * @var array + */ + protected $data; + + /** + * @var PHPUnit_Extensions_Database_DataSet_ITable|null + */ + private $other; + + /** + * Sets the metadata for this table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData + * @deprecated + */ + protected function setTableMetaData(PHPUnit_Extensions_Database_DataSet_ITableMetaData $tableMetaData) + { + $this->tableMetaData = $tableMetaData; + } + + /** + * Returns the table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData() + { + return $this->tableMetaData; + } + + /** + * Returns the number of rows in this table. + * + * @return int + */ + public function getRowCount() + { + return count($this->data); + } + + /** + * Returns the value for the given column on the given row. + * + * @param int $row + * @param int $column + * @todo reorganize this function to throw the exception first. + */ + public function getValue($row, $column) + { + if (isset($this->data[$row][$column])) { + $value = $this->data[$row][$column]; + + return ($value instanceof SimpleXMLElement) ? (string) $value : $value; + } else { + if (!in_array($column, $this->getTableMetaData()->getColumns()) || $this->getRowCount() <= $row) { + throw new InvalidArgumentException("The given row ({$row}) and column ({$column}) do not exist in table {$this->getTableMetaData()->getTableName()}"); + } else { + return NULL; + } + } + } + + /** + * Returns the an associative array keyed by columns for the given row. + * + * @param int $row + * @return array + */ + public function getRow($row) + { + if (isset($this->data[$row])) { + return $this->data[$row]; + } else { + if ($this->getRowCount() <= $row) { + throw new InvalidArgumentException("The given row ({$row}) does not exist in table {$this->getTableMetaData()->getTableName()}"); + } else { + return NULL; + } + } + } + + /** + * Asserts that the given table matches this table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other) + { + $thisMetaData = $this->getTableMetaData(); + $otherMetaData = $other->getTableMetaData(); + + if (!$thisMetaData->matches($otherMetaData) || + $this->getRowCount() != $other->getRowCount()) { + return FALSE; + } + + $columns = $thisMetaData->getColumns(); + $rowCount = $this->getRowCount(); + + for ($i = 0; $i < $rowCount; $i++) { + foreach ($columns as $columnName) { + $thisValue = $this->getValue($i, $columnName); + $otherValue = $other->getValue($i, $columnName); + if (is_numeric($thisValue) && is_numeric($otherValue)) { + if ($thisValue != $otherValue) { + $this->other = $other; + + return FALSE; + } + } elseif ($thisValue !== $otherValue) { + $this->other = $other; + + return FALSE; + } + } + } + + return TRUE; + } + + /** + * Checks if a given row is in the table + * + * @param array $row + * + * @return bool + */ + public function assertContainsRow(array $row) + { + return in_array($row, $this->data); + } + + public function __toString() + { + $columns = $this->getTableMetaData()->getColumns(); + $lineSeperator = str_repeat('+----------------------', count($columns)) . "+\n"; + $lineLength = strlen($lineSeperator) - 1; + + $tableString = $lineSeperator; + $tableString .= '| ' . str_pad($this->getTableMetaData()->getTableName(), $lineLength - 4, ' ', STR_PAD_RIGHT) . " |\n"; + $tableString .= $lineSeperator; + $tableString .= $this->rowToString($columns); + $tableString .= $lineSeperator; + + $rowCount = $this->getRowCount(); + + for ($i = 0; $i < $rowCount; $i++) { + $values = array(); + + foreach ($columns as $columnName) { + if ($this->other) { + try { + if ($this->getValue($i, $columnName) != $this->other->getValue($i, $columnName)) { + $values[] = sprintf( + '%s != actual %s', + var_export($this->getValue($i, $columnName), TRUE), + var_export($this->other->getValue($i, $columnName), TRUE) + ); + } else { + $values[] = $this->getValue($i, $columnName); + } + } catch (\InvalidArgumentException $ex) { + $values[] = $this->getValue($i, $columnName) . ': no row'; + } + } else { + $values[] = $this->getValue($i, $columnName); + } + } + + $tableString .= $this->rowToString($values) . $lineSeperator; + } + + return ($this->other ? '(table diff enabled)' : '') . "\n" . $tableString . "\n"; + } + + protected function rowToString(Array $row) + { + $rowString = ''; + + foreach ($row as $value) { + if (is_null($value)) { + $value = 'NULL'; + } + + $rowString .= '| ' . str_pad(substr($value, 0, 20), 20, ' ', STR_PAD_BOTH) . ' '; + } + + return $rowString . "|\n"; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates CsvDataSets. + * + * You can incrementally add CSV files as tables to your datasets + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_CsvDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * @var array + */ + protected $tables = array(); + + /** + * @var string + */ + protected $delimiter = ','; + + /** + * @var string + */ + protected $enclosure = '"'; + + /** + * @var string + */ + protected $escape = '"'; + + /** + * Creates a new CSV dataset + * + * You can pass in the parameters for how csv files will be read. + * + * @param string $delimiter + * @param string $enclosure + * @param string $escape + */ + public function __construct($delimiter = ',', $enclosure = '"', $escape = '"') + { + $this->delimiter = $delimiter; + $this->enclosure = $enclosure; + $this->escape = $escape; + } + + /** + * Adds a table to the dataset + * + * The table will be given the passed name. $csvFile should be a path to + * a valid csv file (based on the arguments passed to the constructor.) + * + * @param string $tableName + * @param string $csvFile + */ + public function addTable($tableName, $csvFile) + { + if (!is_file($csvFile)) { + throw new InvalidArgumentException("Could not find csv file: {$csvFile}"); + } + + if (!is_readable($csvFile)) { + throw new InvalidArgumentException("Could not read csv file: {$csvFile}"); + } + + $fh = fopen($csvFile, 'r'); + $columns = $this->getCsvRow($fh); + + if ($columns === FALSE) + { + throw new InvalidArgumentException("Could not determine the headers from the given file {$csvFile}"); + } + + $metaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns); + $table = new PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData); + + while (($row = $this->getCsvRow($fh)) !== FALSE) + { + $table->addRow(array_combine($columns, $row)); + } + + $this->tables[$tableName] = $table; + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected function createIterator($reverse = FALSE) + { + return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse); + } + + /** + * Returns a row from the csv file in an indexed array. + * + * @param resource $fh + * @return array + */ + protected function getCsvRow($fh) + { + if (version_compare(PHP_VERSION, '5.3.0', '>')) { + return fgetcsv($fh, NULL, $this->delimiter, $this->enclosure, $this->escape); + } else { + return fgetcsv($fh, NULL, $this->delimiter, $this->enclosure); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface for creating and reading data from data sets. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DataSet_ITableIterator extends Iterator +{ + /** + * Returns the current table. + * + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function getTable(); + + /** + * Returns the current table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData(); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default implementation of a data set. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_DefaultDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * An array of ITable objects. + * + * @var array + */ + protected $tables; + + /** + * Creates a new dataset using the given tables. + * + * @param array $tables + */ + public function __construct(Array $tables = array()) + { + $this->tables = $tables; + } + + /** + * Adds a table to the dataset. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + */ + public function addTable(PHPUnit_Extensions_Database_DataSet_ITable $table) + { + $this->tables[] = $table; + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected function createIterator($reverse = FALSE) + { + return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface for returning table meta data. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DataSet_ITableMetaData +{ + /** + * Returns the names of the columns in the table. + * + * @return array + */ + public function getColumns(); + + /** + * Returns the names of the primary key columns in the table. + * + * @return array + */ + public function getPrimaryKeys(); + + /** + * Returns the name of the table. + * + * @return string + */ + public function getTableName(); + + /** + * Asserts that the given tableMetaData matches this tableMetaData. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_ITableMetaData $other); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TableMetaData decorator that allows filtering columns from another + * metaData object. + * + * The if a whitelist (include) filter is specified, then only those columns + * will be included. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_TableMetaDataFilter extends PHPUnit_Extensions_Database_DataSet_AbstractTableMetaData +{ + /** + * The table meta data being decorated. + * @var PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + protected $originalMetaData; + + /** + * The columns to exclude from the meta data. + * @var Array + */ + protected $excludeColumns = array(); + + /** + * The columns to include from the meta data. + * @var Array + */ + protected $includeColumns = array(); + + /** + * Creates a new filtered table meta data object filtering out + * $excludeColumns. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $originalMetaData + * @param array $excludeColumns - Deprecated. Use the set* methods instead. + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_ITableMetaData $originalMetaData, Array $excludeColumns = array()) + { + $this->originalMetaData = $originalMetaData; + $this->addExcludeColumns($excludeColumns); + } + + /** + * Returns the names of the columns in the table. + * + * @return array + */ + public function getColumns() + { + if (!empty($this->includeColumns)) { + return array_values(array_intersect($this->originalMetaData->getColumns(), $this->includeColumns)); + } + elseif (!empty($this->excludeColumns)) { + return array_values(array_diff($this->originalMetaData->getColumns(), $this->excludeColumns)); + } + else { + return $this->originalMetaData->getColumns(); + } + } + + /** + * Returns the names of the primary key columns in the table. + * + * @return array + */ + public function getPrimaryKeys() + { + return $this->originalMetaData->getPrimaryKeys(); + } + + /** + * Returns the name of the table. + * + * @return string + */ + public function getTableName() + { + return $this->originalMetaData->getTableName(); + } + + /** + * Sets the columns to include in the table. + * @param Array $includeColumns + */ + public function addIncludeColumns(Array $includeColumns) + { + $this->includeColumns = array_unique(array_merge($this->includeColumns, $includeColumns)); + } + + /** + * Clears the included columns. + */ + public function clearIncludeColumns() + { + $this->includeColumns = array(); + } + + /** + * Sets the columns to exclude from the table. + * @param Array $excludeColumns + */ + public function addExcludeColumns(Array $excludeColumns) + { + $this->excludeColumns = array_unique(array_merge($this->excludeColumns, $excludeColumns)); + } + + /** + * Clears the excluded columns. + */ + public function clearExcludeColumns() + { + $this->excludeColumns = array(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default table iterator + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_ReplacementTableIterator implements OuterIterator, PHPUnit_Extensions_Database_DataSet_ITableIterator +{ + /** + * @var PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected $innerIterator; + + /** + * @var array + */ + protected $fullReplacements; + + /** + * @var array + */ + protected $subStrReplacements; + + /** + * Creates a new replacement table iterator object. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableIterator $innerIterator + * @param array $fullReplacements + * @param array $subStrReplacements + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_ITableIterator $innerIterator, Array $fullReplacements = array(), Array $subStrReplacements = array()) + { + $this->innerIterator = $innerIterator; + $this->fullReplacements = $fullReplacements; + $this->subStrReplacements = $subStrReplacements; + } + + /** + * Adds a new full replacement + * + * Full replacements will only replace values if the FULL value is a match + * + * @param string $value + * @param string $replacement + */ + public function addFullReplacement($value, $replacement) + { + $this->fullReplacements[$value] = $replacement; + } + + /** + * Adds a new substr replacement + * + * Substr replacements will replace all occurances of the substr in every column + * + * @param string $value + * @param string $replacement + */ + public function addSubStrReplacement($value, $replacement) + { + $this->subStrReplacements[$value] = $replacement; + } + + /** + * Returns the current table. + * + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function getTable() + { + return $this->current(); + } + + /** + * Returns the current table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData() + { + $this->current()->getTableMetaData(); + } + + /** + * Returns the current table. + * + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function current() + { + return new PHPUnit_Extensions_Database_DataSet_ReplacementTable($this->innerIterator->current(), $this->fullReplacements, $this->subStrReplacements); + } + + /** + * Returns the name of the current table. + * + * @return string + */ + public function key() + { + return $this->current()->getTableMetaData()->getTableName(); + } + + /** + * advances to the next element. + */ + public function next() + { + $this->innerIterator->next(); + } + + /** + * Rewinds to the first element + */ + public function rewind() + { + $this->innerIterator->rewind(); + } + + /** + * Returns true if the current index is valid + * + * @return bool + */ + public function valid() + { + return $this->innerIterator->valid(); + } + + public function getInnerIterator() + { + return $this->innerIterator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default YAML parser, using Symfony/Yaml. + * + * @since Class available since Release 1.3.1 + */ +class PHPUnit_Extensions_Database_DataSet_SymfonyYamlParser implements PHPUnit_Extensions_Database_DataSet_IYamlParser { + public function parseYaml($yamlFile) { + return Symfony\Component\Yaml\Yaml::parse(file_get_contents($yamlFile)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates YamlDataSets. + * + * You can incrementally add YAML files as tables to your datasets + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_YamlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * @var array + */ + protected $tables = array(); + + /** + * @var PHPUnit_Extensions_Database_DataSet_IYamlParser + */ + protected $parser; + + /** + * Creates a new YAML dataset + * + * @param string $yamlFile + * @param PHPUnit_Extensions_Database_DataSet_IYamlParser $parser + */ + public function __construct($yamlFile, $parser = NULL) + { + if ($parser == NULL) { + $parser = new PHPUnit_Extensions_Database_DataSet_SymfonyYamlParser(); + } + $this->parser = $parser; + $this->addYamlFile($yamlFile); + } + + /** + * Adds a new yaml file to the dataset. + * @param string $yamlFile + */ + public function addYamlFile($yamlFile) + { + $data = $this->parser->parseYaml($yamlFile); + + foreach ($data as $tableName => $rows) { + if (!isset($rows)) { + $rows = array(); + } + + if (!is_array($rows)) { + continue; + } + + if (!array_key_exists($tableName, $this->tables)) { + $columns = $this->getColumns($rows); + + $tableMetaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData( + $tableName, $columns + ); + + $this->tables[$tableName] = new PHPUnit_Extensions_Database_DataSet_DefaultTable( + $tableMetaData + ); + } + + foreach ($rows as $row) { + $this->tables[$tableName]->addRow($row); + } + } + } + + /** + * Creates a unique list of columns from all the rows in a table. + * If the table is defined another time in the Yaml, and if the Yaml + * parser could return the multiple occerrences, then this would be + * insufficient unless we grouped all the occurences of the table + * into onwe row set. sfYaml, however, does not provide multiple tables + * with the same name, it only supplies the last table. + * + * @params all the rows in a table. + */ + private function getColumns($rows) { + $columns = array(); + + foreach ($rows as $row) { + $columns = array_merge($columns, array_keys($row)); + } + + return array_values(array_unique($columns)); + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected function createIterator($reverse = FALSE) + { + return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator( + $this->tables, $reverse + ); + } + + /** + * Saves a given $dataset to $filename in YAML format + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset + * @param string $filename + */ + public static function write(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset, $filename) + { + $pers = new PHPUnit_Extensions_Database_DataSet_Persistors_Yaml(); + $pers->setFileName($filename); + + try { + $pers->write($dataset); + } + + catch (RuntimeException $e) { + throw new PHPUnit_Framework_Exception( + __METHOD__ . ' called with an unwritable file.' + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default implementation of a data set. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_DataSet_AbstractXmlDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * @var array + */ + protected $tables; + + /** + * @var SimpleXmlElement + */ + protected $xmlFileContents; + + /** + * Creates a new dataset using the given tables. + * + * @param array $tables + */ + public function __construct($xmlFile) + { + if (!is_file($xmlFile)) { + throw new InvalidArgumentException( + "Could not find xml file: {$xmlFile}" + ); + } + + $libxmlErrorReporting = libxml_use_internal_errors(TRUE); + $this->xmlFileContents = simplexml_load_file($xmlFile, 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_PARSEHUGE); + + if (!$this->xmlFileContents) { + $message = ''; + + foreach (libxml_get_errors() as $error) { + $message .= print_r($error, true); + } + + throw new RuntimeException($message); + } + + libxml_clear_errors(); + libxml_use_internal_errors($libxmlErrorReporting); + + $tableColumns = array(); + $tableValues = array(); + + $this->getTableInfo($tableColumns, $tableValues); + $this->createTables($tableColumns, $tableValues); + } + + /** + * Reads the simple xml object and creates the appropriate tables and meta + * data for this dataset. + */ + protected abstract function getTableInfo(Array &$tableColumns, Array &$tableValues); + + protected function createTables(Array &$tableColumns, Array &$tableValues) + { + foreach ($tableValues as $tableName => $values) { + $table = $this->getOrCreateTable($tableName, $tableColumns[$tableName]); + foreach ($values as $value) { + $table->addRow($value); + } + } + } + + /** + * Returns the table with the matching name. If the table does not exist + * an empty one is created. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + protected function getOrCreateTable($tableName, $tableColumns) + { + if (empty($this->tables[$tableName])) { + $tableMetaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $tableColumns); + $this->tables[$tableName] = new PHPUnit_Extensions_Database_DataSet_DefaultTable($tableMetaData); + } + + return $this->tables[$tableName]; + } + + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected function createIterator($reverse = FALSE) + { + return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Implements the basic functionality of data sets. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_DataSet_AbstractDataSet implements PHPUnit_Extensions_Database_DataSet_IDataSet +{ + /** + * Creates an iterator over the tables in the data set. If $reverse is + * true a reverse iterator will be returned. + * + * @param bool $reverse + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + protected abstract function createIterator($reverse = FALSE); + + /** + * Returns an array of table names contained in the dataset. + * + * @return array + */ + public function getTableNames() + { + $tableNames = array(); + + foreach ($this->getIterator() as $table) { + /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */ + $tableNames[] = $table->getTableMetaData()->getTableName(); + } + + return $tableNames; + } + + /** + * Returns a table meta data object for the given table. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData($tableName) + { + return $this->getTable($tableName)->getTableMetaData(); + } + + /** + * Returns a table object for the given table. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function getTable($tableName) + { + foreach ($this->getIterator() as $table) { + /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */ + if ($table->getTableMetaData()->getTableName() == $tableName) { + return $table; + } + } + } + + /** + * Returns an iterator for all table objects in the given dataset. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + public function getIterator() + { + return $this->createIterator(); + } + + /** + * Returns a reverse iterator for all table objects in the given dataset. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + public function getReverseIterator() + { + return $this->createIterator(TRUE); + } + + /** + * Asserts that the given data set matches this data set. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_IDataSet $other) + { + $thisTableNames = $this->getTableNames(); + $otherTableNames = $other->getTableNames(); + + sort($thisTableNames); + sort($otherTableNames); + + if ($thisTableNames != $otherTableNames) { + return FALSE; + } + + foreach ($thisTableNames as $tableName) { + $table = $this->getTable($tableName); + + if (!$table->matches($other->getTable($tableName))) { + return FALSE; + } + } + + return TRUE; + } + + public function __toString() + { + $iterator = $this->getIterator(); + + $dataSetString = ''; + foreach ($iterator as $table) { + $dataSetString .= $table->__toString(); + } + + return $dataSetString; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface for creating and reading data from data sets. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DataSet_IDataSet extends IteratorAggregate +{ + /** + * Returns an array of table names contained in the dataset. + * + * @return array + */ + public function getTableNames(); + + /** + * Returns a table meta data object for the given table. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData($tableName); + + /** + * Returns a table object for the given table. + * + * @param string $tableName + * @return PHPUnit_Extensions_Database_DataSet_ITable + */ + public function getTable($tableName); + + /** + * Returns a reverse iterator for all table objects in the given dataset. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableIterator + */ + public function getReverseIterator(); + + /** + * Asserts that the given data set matches this data set. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_IDataSet $other); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides basic functionality for table meta data. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_DataSet_AbstractTableMetaData implements PHPUnit_Extensions_Database_DataSet_ITableMetaData +{ + /** + * The names of all columns in the table. + * + * @var Array + */ + protected $columns; + + /** + * The names of all the primary keys in the table. + * + * @var Array + */ + protected $primaryKeys; + + /** + * @var string + */ + protected $tableName; + + /** + * Returns the names of the columns in the table. + * + * @return array + */ + public function getColumns() + { + return $this->columns; + } + + /** + * Returns the names of the primary key columns in the table. + * + * @return array + */ + public function getPrimaryKeys() + { + return $this->primaryKeys; + } + + /** + * Returns the name of the table. + * + * @return string + */ + public function getTableName() + { + return $this->tableName; + } + + /** + * Asserts that the given tableMetaData matches this tableMetaData. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_ITableMetaData $other) + { + if ($this->getTableName() != $other->getTableName() || + $this->getColumns() != $other->getColumns()) { + return FALSE; + } + + return TRUE; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides the functionality to represent a database table. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_QueryTable extends PHPUnit_Extensions_Database_DataSet_AbstractTable +{ + /** + * @var string + */ + protected $query; + + /** + * @var PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + protected $databaseConnection; + + /** + * @var string + */ + protected $tableName; + + /** + * Creates a new database query table object. + * + * @param string $table_name + * @param string $query + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection + */ + public function __construct($tableName, $query, PHPUnit_Extensions_Database_DB_IDatabaseConnection $databaseConnection) + { + $this->query = $query; + $this->databaseConnection = $databaseConnection; + $this->tableName = $tableName; + } + + /** + * Returns the table's meta data. + * + * @return PHPUnit_Extensions_Database_DataSet_ITableMetaData + */ + public function getTableMetaData() + { + $this->createTableMetaData(); + + return parent::getTableMetaData(); + } + + /** + * Checks if a given row is in the table + * + * @param array $row + * + * @return bool + */ + public function assertContainsRow(Array $row) + { + $this->loadData(); + + return parent::assertContainsRow($row); + } + + /** + * Returns the number of rows in this table. + * + * @return int + */ + public function getRowCount() + { + $this->loadData(); + + return parent::getRowCount(); + } + + /** + * Returns the value for the given column on the given row. + * + * @param int $row + * @param int $column + */ + public function getValue($row, $column) + { + $this->loadData(); + + return parent::getValue($row, $column); + } + + /** + * Returns the an associative array keyed by columns for the given row. + * + * @param int $row + * @return array + */ + public function getRow($row) + { + $this->loadData(); + + return parent::getRow($row); + } + + /** + * Asserts that the given table matches this table. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $other + */ + public function matches(PHPUnit_Extensions_Database_DataSet_ITable $other) + { + $this->loadData(); + + return parent::matches($other); + } + + protected function loadData() + { + if ($this->data === NULL) { + $pdoStatement = $this->databaseConnection->getConnection()->query($this->query); + $this->data = $pdoStatement->fetchAll(PDO::FETCH_ASSOC); + } + } + + protected function createTableMetaData() + { + if ($this->tableMetaData === NULL) + { + $this->loadData(); + + // if some rows are in the table + $columns = array(); + if (isset($this->data[0])) + // get column names from data + $columns = array_keys($this->data[0]); + else { + // if no rows found, get column names from database + $pdoStatement = $this->databaseConnection->getConnection()->prepare('SELECT column_name FROM information_schema.COLUMNS WHERE table_schema=:schema AND table_name=:table'); + $pdoStatement->execute(array( + 'table' => $this->tableName, + 'schema' => $this->databaseConnection->getSchema() + )); + + $columns = $pdoStatement->fetchAll(PDO::FETCH_COLUMN, 0); + } + // create metadata + $this->tableMetaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($this->tableName, $columns); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Implements the basic functionality of data sets using a PHP array. + * + * @since Class available since Release 1.3.2 + */ +class PHPUnit_Extensions_Database_DataSet_ArrayDataSet extends PHPUnit_Extensions_Database_DataSet_AbstractDataSet +{ + /** + * @var array + */ + protected $tables = array(); + + /** + * Constructor to build a new ArrayDataSet with the given array. + * The array parameter is an associative array of tables where the key is + * the table name and the value an array of rows. Each row is an associative + * array by itself with keys representing the field names and the values the + * actual data. + * For example: + * array( + * "addressbook" => array( + * array("id" => 1, "name" => "...", "address" => "..."), + * array("id" => 2, "name" => "...", "address" => "...") + * ) + * ) + * + * @param array $data + */ + public function __construct(array $data) + { + foreach ($data AS $tableName => $rows) { + $columns = array(); + if (isset($rows[0])) { + $columns = array_keys($rows[0]); + } + + $metaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns); + $table = new PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData); + + foreach ($rows AS $row) { + $table->addRow($row); + } + $this->tables[$tableName] = $table; + } + } + + protected function createIterator($reverse = FALSE) + { + return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse); + } + + public function getTable($tableName) + { + if (!isset($this->tables[$tableName])) { + throw new InvalidArgumentException("$tableName is not a table in the current database."); + } + + return $this->tables[$tableName]; + } +} +?> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An interface for parsing YAML files. + * + * @since Class available since Release 1.3.1 + */ +interface PHPUnit_Extensions_Database_DataSet_IYamlParser { + /** + * @param string $yamlFile + * @return array parsed YAML + */ + public function parseYaml($yamlFile); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates CsvDataSets based off of a spec string. + * + * The format of the spec string is as follows: + * + * |table1:filename.csv,table2:filename2.csv + * + * The first portion of the spec including the pipe symbol '|' is optional. + * If the pipe option is included than it may be preceded by up to four + * characters specifying values for the following arguments in order: + * delimiter (defaults to ',',) enclosure (defaults to '"',) escape (defaults to '"',). + * + * Any additional characters in the csv options will be discarded. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Specs_Csv implements PHPUnit_Extensions_Database_DataSet_ISpec +{ + /** + * Creates CSV Data Set from a data set spec. + * + * @param string $dataSetSpec + * @return PHPUnit_Extensions_Database_DataSet_CsvDataSet + */ + public function getDataSet($dataSetSpec) + { + $csvDataSetArgs = $this->getCsvOptions($dataSetSpec); + $csvDataSetRfl = new ReflectionClass('PHPUnit_Extensions_Database_DataSet_CsvDataSet'); + $csvDataSet = $csvDataSetRfl->newInstanceArgs($csvDataSetArgs); + + foreach ($this->getTableFileMap($dataSetSpec) as $tableName => $file) { + $csvDataSet->addTable($tableName, $file); + } + + return $csvDataSet; + } + + /** + * Returns CSV options. + * + * Returns an array containing the options that will be passed to the + * PHPUnit_Extensions_Database_DataSet_CsvDataSet constructor. The options + * are determined by the given $dataSetSpec. + * + * @param string $dataSetSpec + * @return array + */ + protected function getCsvOptions($dataSetSpec) + { + list($csvOptStr) = explode('|', $dataSetSpec, 2); + + return str_split($csvOptStr); + } + + /** + * Returns map of tables to files. + * + * Returns an associative array containing a mapping of tables (the key) + * to files (the values.) The tables and files are determined by the given + * $dataSetSpec + * + * @param string $dataSetSpec + * @return array + */ + protected function getTableFileMap($dataSetSpec) + { + $tables = array(); + + foreach (explode(',', $dataSetSpec) as $csvfile) { + list($tableName, $file) = explode(':', $csvfile, 2); + $tables[$tableName] = $file; + } + + return $tables; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An interface for data set spec factories. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_DataSet_Specs_IFactory +{ + /** + * Returns the data set + * + * @param string $type + * @return PHPUnit_Extensions_Database_DataSet_ISpec + */ + public function getDataSetSpecByType($type); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates the appropriate DataSet Spec based on a given type. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Specs_Factory implements PHPUnit_Extensions_Database_DataSet_Specs_IFactory +{ + /** + * Returns the data set + * + * @param string $type + * @return PHPUnit_Extensions_Database_DataSet_ISpec + */ + public function getDataSetSpecByType($type) + { + switch ($type) { + case 'xml': + return new PHPUnit_Extensions_Database_DataSet_Specs_Xml(); + + case 'flatxml': + return new PHPUnit_Extensions_Database_DataSet_Specs_FlatXml(); + + case 'csv': + return new PHPUnit_Extensions_Database_DataSet_Specs_Csv(); + + case 'yaml': + return new PHPUnit_Extensions_Database_DataSet_Specs_Yaml(); + + case 'dbtable': + return new PHPUnit_Extensions_Database_DataSet_Specs_DbTable(); + + case 'dbquery': + return new PHPUnit_Extensions_Database_DataSet_Specs_DbQuery(); + + default: + throw new PHPUnit_Extensions_Database_Exception("I don't know what you want from me."); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates a YAML dataset based off of a spec string. + * + * The format of the spec string is as follows: + * + * + * + * The filename should be the location of a yaml file relative to the + * current working directory. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Specs_Yaml implements PHPUnit_Extensions_Database_DataSet_ISpec +{ + /** + * Creates YAML Data Set from a data set spec. + * + * @param string $dataSetSpec + * @return PHPUnit_Extensions_Database_DataSet_YamlDataSet + */ + public function getDataSet($dataSetSpec) + { + return new PHPUnit_Extensions_Database_DataSet_YamlDataSet($dataSetSpec); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates a FlatXML dataset based off of a spec string. + * + * The format of the spec string is as follows: + * + * + * + * The filename should be the location of a flat xml file relative to the + * current working directory. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Specs_FlatXml implements PHPUnit_Extensions_Database_DataSet_ISpec +{ + /** + * Creates Flat XML Data Set from a data set spec. + * + * @param string $dataSetSpec + * @return PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet + */ + public function getDataSet($dataSetSpec) + { + return new PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet($dataSetSpec); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates a database dataset based off of a spec string. + * + * This spec class requires a list of databases to be set to the object before + * it can return a list of databases. + * + * The format of the spec string is as follows: + * + * :: + * + * The db label should be equal to one of the keys in the array of databases + * passed to setDatabases(). + * + * The schema should be the primary schema you will be choosing tables from. + * + * The tables should be a comma delimited list of all tables you would like to + * pull data from. + * + * The sql is the query you want to use to generate the table columns and data. + * The column names in the table will be identical to the column aliases in the + * query. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Specs_DbTable implements PHPUnit_Extensions_Database_DataSet_ISpec, PHPUnit_Extensions_Database_IDatabaseListConsumer +{ + /** + * @var array + */ + protected $databases = array(); + + /** + * Sets the database for the spec + * + * @param array $databases + */ + public function setDatabases(array $databases) + { + $this->databases = $databases; + } + + /** + * Creates a DB Data Set from a data set spec. + * + * @param string $dataSetSpec + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + public function getDataSet($dataSetSpec) + { + list($dbLabel, $schema, $tables) = explode(':', $dataSetSpec, 3); + $databaseInfo = $this->databases[$dbLabel]; + + $pdoRflc = new ReflectionClass('PDO'); + $pdo = $pdoRflc->newInstanceArgs(explode('|', $databaseInfo)); + $dbConnection = new PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection($pdo, $schema); + + return !empty($tables) ? $dbConnection->createDataSet(explode(',', $tables)) : $dbConnection->createDataSet(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates DefaultDataSets based off of a spec string. + * + * This spec class requires a list of databases to be set to the object before + * it can return a list of databases. + * + * The format of the spec string is as follows: + * + * ::
: + * + * The db label should be equal to one of the keys in the array of databases + * passed to setDatabases(). + * + * The schema should be the primary schema you will be running the sql query + * against. + * + * The table name should be set to what you would like the table name in the + * dataset to be. + * + * The sql is the query you want to use to generate the table columns and data. + * The column names in the table will be identical to the column aliases in the + * query. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Specs_DbQuery implements PHPUnit_Extensions_Database_DataSet_ISpec, PHPUnit_Extensions_Database_IDatabaseListConsumer +{ + /** + * @var array + */ + protected $databases = array(); + + /** + * Sets the database for the spec + * + * @param array $databases + */ + public function setDatabases(array $databases) + { + $this->databases = $databases; + } + + /** + * Creates a Default Data Set with a query table from a data set spec. + * + * @param string $dataSetSpec + * @return PHPUnit_Extensions_Database_DataSet_DefaultDataSet + */ + public function getDataSet($dataSetSpec) + { + list($dbLabel, $schema, $table, $sql) = explode(':', $dataSetSpec, 4); + $databaseInfo = $this->databases[$dbLabel]; + + $pdoRflc = new ReflectionClass('PDO'); + $pdo = $pdoRflc->newInstanceArgs(explode('|', $databaseInfo)); + $dbConnection = new PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection($pdo, $schema); + $table = $dbConnection->createQueryTable($table, $sql); + + return new PHPUnit_Extensions_Database_DataSet_DefaultDataSet(array($table)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates a XML dataset based off of a spec string. + * + * The format of the spec string is as follows: + * + * + * + * The filename should be the location of a xml file relative to the + * current working directory. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_DataSet_Specs_Xml implements PHPUnit_Extensions_Database_DataSet_ISpec +{ + /** + * Creates XML Data Set from a data set spec. + * + * @param string $dataSetSpec + * @return PHPUnit_Extensions_Database_DataSet_XmlDataSet + */ + public function getDataSet($dataSetSpec) + { + return new PHPUnit_Extensions_Database_DataSet_XmlDataSet($dataSetSpec); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This is the interface for DatabaseTester objects. These objects are used to + * add database testing to existing test cases using composition instead of + * extension. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_ITester +{ + /** + * Closes the specified connection. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + */ + public function closeConnection(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection); + + /** + * Returns the test database connection. + * + * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + public function getConnection(); + + /** + * Returns the test dataset. + * + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + public function getDataSet(); + + /** + * TestCases must call this method inside setUp(). + */ + public function onSetUp(); + + /** + * TestCases must call this method inside tearDown(). + */ + public function onTearDown(); + + /** + * Sets the test dataset to use. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet + */ + public function setDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet); + + /** + * Sets the schema value. + * + * @param string $schema + */ + public function setSchema($schema); + + /** + * Sets the DatabaseOperation to call when starting the test. + * + * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $setUpOperation + */ + public function setSetUpOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $setUpOperation); + + /** + * Sets the DatabaseOperation to call when stopping the test. + * + * @param PHPUnit_Extensions_Database_Operation_DatabaseOperation $tearDownOperation + */ + public function setTearDownOperation(PHPUnit_Extensions_Database_Operation_IDatabaseOperation $tearDownOperation); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An interface for classes that require a list of databases to operate. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_IDatabaseListConsumer +{ + /** + * Sets the database for the spec + * + * @param array $databases + */ + public function setDatabases(array $databases); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Asserts whether or not two dbunit datasets are equal. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Constraint_DataSetIsEqual extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Extensions_Database_DataSet_IDataSet + */ + protected $value; + + /** + * @var string + */ + protected $failure_reason; + + /** + * Creates a new constraint. + * + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $value + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_IDataSet $value) + { + parent::__construct(); + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + if (!$other instanceof PHPUnit_Extensions_Database_DataSet_IDataSet) { + throw new InvalidArgumentException( + 'PHPUnit_Extensions_Database_DataSet_IDataSet expected' + ); + } + + return $this->value->matches($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return $other->__toString() . ' ' . $this->toString(); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'is equal to expected %s', $this->value->__toString() + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Asserts whether or not two dbunit tables are equal. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Constraint_TableIsEqual extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Extensions_Database_DataSet_ITable + */ + protected $value; + + /** + * @var string + */ + protected $failure_reason; + + /** + * Creates a new constraint. + * + * @param PHPUnit_Extensions_Database_DataSet_ITable $value + */ + public function __construct(PHPUnit_Extensions_Database_DataSet_ITable $value) + { + parent::__construct(); + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + if (!$other instanceof PHPUnit_Extensions_Database_DataSet_ITable) { + throw new InvalidArgumentException( + 'PHPUnit_Extensions_Database_DataSet_ITable expected' + ); + } + + return $this->value->matches($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * @return string + */ + protected function failureDescription($other) + { + return $other->__toString() . ' ' . $this->toString(); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'is equal to expected %s', $this->value->__toString() + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Asserts the row count in a table + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Constraint_TableRowCount extends PHPUnit_Framework_Constraint +{ + /** + * @var int + */ + protected $value; + + /** + * @var string + */ + protected $tableName; + + /** + * Creates a new constraint. + * + * @param $tableName + * @param $value + */ + public function __construct($tableName, $value) + { + parent::__construct(); + $this->tableName = $tableName; + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + protected function matches($other) + { + return $other == $this->value; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf('is equal to expected row count %d', $this->value); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An exception thrown when an invalid mode is requested from a mode factory. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_UI_InvalidModeException extends LogicException +{ + /** + * @var string + */ + protected $mode; + + /** + * @var PHPUnit_Extensions_Database_UI_IModeFactory + */ + protected $modeFactory; + + /** + * @param string $mode + * @param string $msg + * @param PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory + */ + public function __construct($mode, $msg, PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory) + { + $this->mode = $mode; + $this->modeFactory = $modeFactory; + parent::__construct($msg); + } + + /** + * @return string + */ + public function getMode() + { + return $this->mode; + } + + /** + * @return array + */ + public function getValidModes() + { + return $this->modeFactory->getModeList(); + } +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The default factory for db extension modes. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_UI_ModeFactory implements PHPUnit_Extensions_Database_UI_IModeFactory +{ + /** + * Generates a new mode based on a given name. + * + * @param string $mode + * @return PHPUnit_Extensions_Database_UI_IMode + */ + public function getMode($mode) + { + if ($mode == '') { + throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'A mode was not provided.', $this); + } + + $modeMap = $this->getModeMap(); + if (isset($modeMap[$mode])) { + $modeClass = $this->getModeClass($mode, $modeMap[$mode]); + + return new $modeClass(); + } else { + throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'The mode does not exist. Attempting to load mode ' . $mode, $this); + } + } + + /** + * Returns the names of valid modes this factory can create. + * + * @return array + */ + public function getModeList() + { + return array_keys($this->getModeMap()); + } + + /** + * Returns a map of modes to class name parts + * + * @return array + */ + protected function getModeMap() + { + return array('export-dataset' => 'ExportDataSet'); + } + + /** + * Given a $mode label and a $mode_name class part attempts to return the + * class name necessary to instantiate the mode. + * + * @param string $mode + * @param string $mode_name + * @return string + */ + protected function getModeClass($mode, $mode_name) + { + $modeClass = 'PHPUnit_Extensions_Database_UI_Modes_' . $mode_name; + $modeFile = dirname(__FILE__) . '/Modes/' . $mode_name . '.php'; + + if (class_exists($modeClass)) { + return $modeClass; + } + + if (!is_readable($modeFile)) { + throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'The mode\'s file could not be loaded. Trying file ' . $modeFile, $this); + } + + require_once ($modeFile); + + if (!class_exists($modeClass)) { + throw new PHPUnit_Extensions_Database_UI_InvalidModeException($mode, 'The mode class was not found in the file. Expecting class name ' . $modeClass, $this); + } + + return $modeClass; + } +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Delegates database extension commands to the appropriate mode classes. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_UI_Command +{ + /** + * @var PHPUnit_Extensions_Database_UI_IModeFactory + */ + protected $modeFactory; + + /** + * @param PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory + */ + public function __construct(PHPUnit_Extensions_Database_UI_IModeFactory $modeFactory) + { + $this->modeFactory = $modeFactory; + } + + /** + * Executes the database extension ui. + * + * @param PHPUnit_Extensions_Database_UI_IMedium $medium + * @param PHPUnit_Extensions_Database_UI_Context $context + */ + public function main(PHPUnit_Extensions_Database_UI_IMedium $medium, PHPUnit_Extensions_Database_UI_Context $context) + { + try { + $medium->buildContext($context); + $mode = $this->modeFactory->getMode($context->getMode()); + $mode->execute($context->getModeArguments(), $medium); + + } catch (Exception $e) { + $medium->handleException($e); + } + } +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Holds the context of a particular database extension ui call. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_UI_Context +{ + /** + * @var string + */ + protected $mode; + + /** + * @var array + */ + protected $modeArguments; + + /** + * @param string $mode + */ + public function setMode($mode) + { + $this->mode = $mode; + } + + /** + * @return string + */ + public function getMode() + { + return $this->mode; + } + + /** + * @param array $arguments + */ + public function setModeArguments(array $arguments) + { + $this->mode_arguments = $arguments; + } + + /** + * @return array + */ + public function getModeArguments() + { + return $this->mode_arguments; + } +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Defines the interface necessary to create new mediums. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_UI_IMedium extends PHPUnit_Extensions_Database_UI_IMediumPrinter +{ + /** + * Builds the context for the application. + * + * @param PHPUnit_Extensions_Database_UI_Context $context + */ + public function buildContext(PHPUnit_Extensions_Database_UI_Context $context); + + /** + * Handles the displaying of exceptions received from the application. + * + * @param Exception $e + */ + public function handleException(Exception $e); +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Defines the interface necessary to create new modes + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_UI_IMode +{ + /** + * Executes the mode using the given arguments and medium. + * + * @param array $modeArguments + * @param PHPUnit_Extensions_Database_UI_IMediumPrinter $medium + */ + public function execute(array $modeArguments, PHPUnit_Extensions_Database_UI_IMediumPrinter $medium); +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Defines the interface necessary to create new mode factories + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_UI_IModeFactory +{ + /** + * Generates a new mode based on a given name. + * + * @param string $mode + * @return PHPUnit_Extensions_Database_UI_IMode + */ + public function getMode($mode); + + /** + * Returns the names of valid modes this factory can create. + * + * @return array + */ + public function getModeList(); +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Defines the interface necessary to create new medium printers. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_UI_IMediumPrinter +{ + /** + * Prints standard output messages. + * + * @param string $message + */ + public function output($message); + + /** + * Prints standard error messages. + * + * @param string $message + */ + public function error($message); +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A text medium for the database extension tool. + * + * This class builds the call context based on command line parameters and + * prints output to stdout and stderr as appropriate. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_UI_Mediums_Text implements PHPUnit_Extensions_Database_UI_IMedium +{ + /** + * @var array + */ + protected $arguments; + + /** + * @var string + */ + protected $command; + + /** + * @param array $arguments + */ + public function __construct(Array $arguments) + { + $this->arguments = $arguments; + } + + /** + * Builds the context for the application. + * + * @param PHPUnit_Extensions_Database_UI_Context $context + */ + public function buildContext(PHPUnit_Extensions_Database_UI_Context $context) + { + $arguments = $this->arguments; + $this->command = array_shift($arguments); + + $context->setMode(array_shift($arguments)); + $context->setModeArguments($arguments); + } + + /** + * Handles the displaying of exceptions received from the application. + * + * @param Exception $e + */ + public function handleException(Exception $e) + { + try { + throw $e; + } catch (PHPUnit_Extensions_Database_UI_InvalidModeException $invalidMode) { + if ($invalidMode->getMode() == '') { + $this->error('Please Specify a Command!' . PHP_EOL); + } else { + $this->error('Command Does Not Exist: ' . $invalidMode->getMode() . PHP_EOL); + } + $this->error('Valid Commands:' . PHP_EOL); + + foreach ($invalidMode->getValidModes() as $mode) { + $this->error(' ' . $mode . PHP_EOL); + } + } catch (Exception $e) { + $this->error('Unknown Error: ' . $e->getMessage() . PHP_EOL); + } + } + + /** + * Prints the message to stdout. + * + * @param string $message + */ + public function output($message) + { + echo $message; + } + + /** + * Prints the message to stderr + * + * @param string $message + */ + public function error($message) + { + fputs(STDERR, $message); + } +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The class for the export-dataset command. + * + * This command is used to convert existing data sets or data in the database + * into a valid data set format. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_UI_Modes_ExportDataSet implements PHPUnit_Extensions_Database_UI_IMode +{ + /** + * Executes the export dataset command. + * + * @param array $modeArguments + * @param PHPUnit_Extensions_Database_UI_IMediumPrinter $medium + */ + public function execute(array $modeArguments, PHPUnit_Extensions_Database_UI_IMediumPrinter $medium) + { + $arguments = new PHPUnit_Extensions_Database_UI_Modes_ExportDataSet_Arguments($modeArguments); + + if (FALSE && !$arguments->areValid()) { + throw new InvalidArgumentException('The arguments for this command are incorrect.'); + } + + $datasets = array(); + foreach ($arguments->getArgumentArray('dataset') as $argString) { + $datasets[] = $this->getDataSetFromArgument($argString, $arguments->getDatabases()); + } + + $finalDataset = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet($datasets); + + $outputDataset = $this->getPersistorFromArgument($arguments->getSingleArgument('output')); + $outputDataset->write($finalDataset); + } + + /** + * Returns the correct dataset given an argument containing a dataset spec. + * + * @param string $argString + * @param array $databaseList + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + protected function getDataSetFromArgument($argString, $databaseList) + { + $dataSetSpecFactory = new PHPUnit_Extensions_Database_DataSet_Specs_Factory(); + list($type, $dataSetSpecStr) = explode(':', $argString, 2); + $dataSetSpec = $dataSetSpecFactory->getDataSetSpecByType($type); + + if ($dataSetSpec instanceof PHPUnit_Extensions_Database_IDatabaseListConsumer) { + $dataSetSpec->setDatabases($databaseList); + } + + return $dataSetSpec->getDataSet($dataSetSpecStr); + } + + /** + * Returns the correct persistor given an argument containing a persistor spec. + * + * @param string $argString + * @return PHPUnit_Extensions_Database_DataSet_IPersistable + */ + protected function getPersistorFromArgument($argString) + { + $persistorFactory = new PHPUnit_Extensions_Database_DataSet_Persistors_Factory(); + list($type, $spec) = explode(':', $argString, 2); + + return $persistorFactory->getPersistorBySpec($type, $spec); + } +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Represents arguments received from a medium. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_UI_Modes_ExportDataSet_Arguments +{ + /** + * @var array + */ + protected $arguments = array(); + + /** + * @param array $arguments + */ + public function __construct(array $arguments) + { + foreach ($arguments as $argument) { + list($argName, $argValue) = explode('=', $argument, 2); + + $argName = trim($argName, '-'); + + if (!isset($this->arguments[$argName])) { + $this->arguments[$argName] = array(); + } + + $this->arguments[$argName][] = $argValue; + } + } + + /** + * Returns an array of arguments matching the given $argName + * + * @param string $argName + * @return array + */ + public function getArgumentArray($argName) + { + if ($this->argumentIsSet($argName)) { + return $this->arguments[$argName]; + } else { + return NULL; + } + } + + /** + * Returns a single argument value. + * + * If $argName points to an array the first argument will be returned. + * + * @param string $argName + * @return mixed + */ + public function getSingleArgument($argName) + { + if ($this->argumentIsSet($argName)) { + return reset($this->arguments[$argName]); + } else { + return NULL; + } + } + + /** + * Returns whether an argument is set. + * + * @param string $argName + * @return bool + */ + public function argumentIsSet($argName) + { + return array_key_exists($argName, $this->arguments); + } + + /** + * Returns an array containing the names of all arguments provided. + * + * @return array + */ + public function getArgumentNames() + { + return array_keys($this->arguments); + } + + /** + * Returns an array of database arguments keyed by name. + * + * @todo this should be moved. + * @return array + */ + public function getDatabases() + { + $databases = $this->getArgumentArray('database'); + + $retDb = array(); + foreach ($databases as $db) { + list($name, $arg) = explode(':', $db, 2); + $retDb[$name] = $arg; + } + + return $retDb; + } +} + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This class provides functionality for inserting rows from a dataset into a database. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Insert extends PHPUnit_Extensions_Database_Operation_RowBased +{ + protected $operationName = 'INSERT'; + + protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + $columnCount = count($table->getTableMetaData()->getColumns()); + + if ($columnCount > 0) { + $placeHolders = implode(', ', array_fill(0, $columnCount, '?')); + + $columns = ''; + foreach ($table->getTableMetaData()->getColumns() as $column) { + $columns .= $connection->quoteSchemaObject($column) . ', '; + } + + $columns = substr($columns, 0, -2); + + $query = " + INSERT INTO {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())} + ({$columns}) + VALUES + ({$placeHolders}) + "; + + return $query; + } else { + return FALSE; + } + } + + protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row) + { + $args = array(); + foreach ($table->getTableMetaData()->getColumns() as $columnName) { + $args[] = $table->getValue($row, $columnName); + } + + return $args; + } + + protected function disablePrimaryKeys(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + if (count($databaseTableMetaData->getPrimaryKeys())) { + return TRUE; + } + + return FALSE; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This class facilitates combining database operations. To create a composite + * operation pass an array of classes that implement + * PHPUnit_Extensions_Database_Operation_IDatabaseOperation and they will be + * executed in that order against all data sets. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Composite implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation +{ + /** + * @var array + */ + protected $operations = array(); + + /** + * Creates a composite operation. + * + * @param array $operations + */ + public function __construct(Array $operations) + { + foreach ($operations as $operation) { + if ($operation instanceof PHPUnit_Extensions_Database_Operation_IDatabaseOperation) { + $this->operations[] = $operation; + } else { + throw new InvalidArgumentException('Only database operation instances can be passed to a composite database operation.'); + } + } + } + + public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + try { + foreach ($this->operations as $operation) { + /* @var $operation PHPUnit_Extensions_Database_Operation_IDatabaseOperation */ + $operation->execute($connection, $dataSet); + } + } catch (PHPUnit_Extensions_Database_Operation_Exception $e) { + throw new PHPUnit_Extensions_Database_Operation_Exception("COMPOSITE[{$e->getOperation()}]", $e->getQuery(), $e->getArgs(), $e->getTable(), $e->getError()); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Updates the rows in a given dataset using primary key columns. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Replace extends PHPUnit_Extensions_Database_Operation_RowBased +{ + protected $operationName = 'REPLACE'; + + protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + $keys = $databaseTableMetaData->getPrimaryKeys(); + + $whereStatement = 'WHERE ' . implode(' AND ', $this->buildPreparedColumnArray($keys, $connection)); + + $query = " + SELECT COUNT(*) + FROM {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())} + {$whereStatement} + "; + + return $query; + } + + protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row) + { + $args = array(); + + foreach ($databaseTableMetaData->getPrimaryKeys() as $columnName) { + $args[] = $table->getValue($row, $columnName); + } + + return $args; + } + + /** + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet + */ + public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + $insertOperation = new PHPUnit_Extensions_Database_Operation_Insert; + $updateOperation = new PHPUnit_Extensions_Database_Operation_Update; + $databaseDataSet = $connection->createDataSet(); + + foreach ($dataSet as $table) { + /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */ + $databaseTableMetaData = $databaseDataSet->getTableMetaData($table->getTableMetaData()->getTableName()); + + $insertQuery = $insertOperation->buildOperationQuery($databaseTableMetaData, $table, $connection); + $updateQuery = $updateOperation->buildOperationQuery($databaseTableMetaData, $table, $connection); + $selectQuery = $this->buildOperationQuery($databaseTableMetaData, $table, $connection); + + $insertStatement = $connection->getConnection()->prepare($insertQuery); + $updateStatement = $connection->getConnection()->prepare($updateQuery); + $selectStatement = $connection->getConnection()->prepare($selectQuery); + + $rowCount = $table->getRowCount(); + + for ($i = 0; $i < $rowCount; $i++) { + $selectArgs = $this->buildOperationArguments($databaseTableMetaData, $table, $i); + $query = $selectQuery; + $args = $selectArgs; + + try { + $selectStatement->execute($selectArgs); + + if ($selectStatement->fetchColumn(0) > 0) { + $updateArgs = $updateOperation->buildOperationArguments($databaseTableMetaData, $table, $i); + $query = $updateQuery; + $args = $updateArgs; + + $updateStatement->execute($updateArgs); + } else { + $insertArgs = $insertOperation->buildOperationArguments($databaseTableMetaData, $table, $i); + $query = $insertQuery; + $args = $insertArgs; + + $insertStatement->execute($insertArgs); + } + } + + catch (Exception $e) { + throw new PHPUnit_Extensions_Database_Operation_Exception( + $this->operationName, $query, $args, $table, $e->getMessage() + ); + } + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Executes a truncate against all tables in a dataset. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Truncate implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation +{ + protected $useCascade = FALSE; + + public function setCascade($cascade = TRUE) + { + $this->useCascade = $cascade; + } + + public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + foreach ($dataSet->getReverseIterator() as $table) { + /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */ + $query = " + {$connection->getTruncateCommand()} {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())} + "; + + if ($this->useCascade && $connection->allowsCascading()) { + $query .= ' CASCADE'; + } + + try { + $this->disableForeignKeyChecksForMysql($connection); + $connection->getConnection()->query($query); + $this->enableForeignKeyChecksForMysql($connection); + } catch (\Exception $e) { + $this->enableForeignKeyChecksForMysql($connection); + + if ($e instanceof PDOException) { + throw new PHPUnit_Extensions_Database_Operation_Exception('TRUNCATE', $query, array(), $table, $e->getMessage()); + } + + throw $e; + } + } + } + + private function disableForeignKeyChecksForMysql(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + if ($this->isMysql($connection)) { + $connection->getConnection()->query('SET FOREIGN_KEY_CHECKS = 0'); + } + } + + private function enableForeignKeyChecksForMysql(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + if ($this->isMysql($connection)) { + $connection->getConnection()->query('SET FOREIGN_KEY_CHECKS = 1'); + } + } + + private function isMysql(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + return $connection->getConnection()->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A class factory to easily return database operations. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Factory +{ + /** + * Returns a null database operation + * + * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + public static function NONE() + { + return new PHPUnit_Extensions_Database_Operation_Null(); + } + + /** + * Returns a clean insert database operation. It will remove all contents + * from the table prior to re-inserting rows. + * + * @param bool $cascadeTruncates Set to true to force truncates to cascade on databases that support this. + * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + public static function CLEAN_INSERT($cascadeTruncates = FALSE) + { + return new PHPUnit_Extensions_Database_Operation_Composite(array( + self::TRUNCATE($cascadeTruncates), + self::INSERT() + )); + } + + /** + * Returns an insert database operation. + * + * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + public static function INSERT() + { + return new PHPUnit_Extensions_Database_Operation_Insert(); + } + + /** + * Returns a truncate database operation. + * + * @param bool $cascadeTruncates Set to true to force truncates to cascade on databases that support this. + * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + public static function TRUNCATE($cascadeTruncates = FALSE) + { + $truncate = new PHPUnit_Extensions_Database_Operation_Truncate(); + $truncate->setCascade($cascadeTruncates); + + return $truncate; + } + + /** + * Returns a delete database operation. + * + * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + public static function DELETE() + { + return new PHPUnit_Extensions_Database_Operation_Delete(); + } + + /** + * Returns a delete_all database operation. + * + * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + public static function DELETE_ALL() + { + return new PHPUnit_Extensions_Database_Operation_DeleteAll(); + } + + /** + * Returns an update database operation. + * + * @return PHPUnit_Extensions_Database_Operation_IDatabaseOperation + */ + public static function UPDATE() + { + return new PHPUnit_Extensions_Database_Operation_Update(); + } + +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This class represents a null database operation. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Null implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation +{ + public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + /* do nothing */ + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides basic functionality for row based operations. + * + * To create a row based operation you must create two functions. The first + * one, buildOperationQuery(), must return a query that will be used to create + * a prepared statement. The second one, buildOperationArguments(), should + * return an array containing arguments for each row. + * + * @since Class available since Release 1.0.0 + */ +abstract class PHPUnit_Extensions_Database_Operation_RowBased implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation +{ + const ITERATOR_TYPE_FORWARD = 0; + const ITERATOR_TYPE_REVERSE = 1; + + protected $operationName; + + protected $iteratorDirection = self::ITERATOR_TYPE_FORWARD; + + /** + * @return string|bool String containing the query or FALSE if a valid query cannot be constructed + */ + protected abstract function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection); + + protected abstract function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row); + + /** + * Allows an operation to disable primary keys if necessary. + * + * @param PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData + * @param PHPUnit_Extensions_Database_DataSet_ITable $table + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + */ + protected function disablePrimaryKeys(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + return FALSE; + } + + /** + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet + */ + public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + $databaseDataSet = $connection->createDataSet(); + + $dsIterator = $this->iteratorDirection == self::ITERATOR_TYPE_REVERSE ? $dataSet->getReverseIterator() : $dataSet->getIterator(); + + foreach ($dsIterator as $table) { + $rowCount = $table->getRowCount(); + + if($rowCount == 0) continue; + + /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */ + $databaseTableMetaData = $databaseDataSet->getTableMetaData($table->getTableMetaData()->getTableName()); + $query = $this->buildOperationQuery($databaseTableMetaData, $table, $connection); + $disablePrimaryKeys = $this->disablePrimaryKeys($databaseTableMetaData, $table, $connection); + + if ($query === FALSE) { + if ($table->getRowCount() > 0) { + throw new PHPUnit_Extensions_Database_Operation_Exception($this->operationName, '', array(), $table, 'Rows requested for insert, but no columns provided!'); + } + continue; + } + + if ($disablePrimaryKeys) { + $connection->disablePrimaryKeys($databaseTableMetaData->getTableName()); + } + + $statement = $connection->getConnection()->prepare($query); + + for ($i = 0; $i < $rowCount; $i++) { + $args = $this->buildOperationArguments($databaseTableMetaData, $table, $i); + + try { + $statement->execute($args); + } + + catch (Exception $e) { + throw new PHPUnit_Extensions_Database_Operation_Exception( + $this->operationName, $query, $args, $table, $e->getMessage() + ); + } + } + + if ($disablePrimaryKeys) { + $connection->enablePrimaryKeys($databaseTableMetaData->getTableName()); + } + } + } + + protected function buildPreparedColumnArray($columns, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + $columnArray = array(); + + foreach ($columns as $columnName) { + $columnArray[] = "{$connection->quoteSchemaObject($columnName)} = ?"; + } + + return $columnArray; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Updates the rows in a given dataset using primary key columns. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Update extends PHPUnit_Extensions_Database_Operation_RowBased +{ + protected $operationName = 'UPDATE'; + + protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + $keys = $databaseTableMetaData->getPrimaryKeys(); + $columns = $table->getTableMetaData()->getColumns(); + $whereStatement = 'WHERE ' . implode(' AND ', $this->buildPreparedColumnArray($keys, $connection)); + $setStatement = 'SET ' . implode(', ', $this->buildPreparedColumnArray($columns, $connection)); + + $query = " + UPDATE {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())} + {$setStatement} + {$whereStatement} + "; + + return $query; + } + + protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row) + { + $args = array(); + foreach ($table->getTableMetaData()->getColumns() as $columnName) { + $args[] = $table->getValue($row, $columnName); + } + + foreach ($databaseTableMetaData->getPrimaryKeys() as $columnName) { + $args[] = $table->getValue($row, $columnName); + } + + return $args; + } + + protected function disablePrimaryKeys(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + if (count($databaseTableMetaData->getPrimaryKeys())) { + return TRUE; + } + + return FALSE; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides a basic interface and functionality for executing database + * operations against a connection using a specific dataSet. + * + * @since Class available since Release 1.0.0 + */ +interface PHPUnit_Extensions_Database_Operation_IDatabaseOperation +{ + /** + * Executes the database operation against the given $connection for the + * given $dataSet. + * + * @param PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection + * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet + * @throws PHPUnit_Extensions_Database_Operation_Exception + */ + public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Deletes all rows from all tables in a dataset. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_DeleteAll implements PHPUnit_Extensions_Database_Operation_IDatabaseOperation +{ + public function execute(PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection, PHPUnit_Extensions_Database_DataSet_IDataSet $dataSet) + { + foreach ($dataSet->getReverseIterator() as $table) { + /* @var $table PHPUnit_Extensions_Database_DataSet_ITable */ + + $query = " + DELETE FROM {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())} + "; + + try { + $connection->getConnection()->query($query); + } catch (PDOException $e) { + throw new PHPUnit_Extensions_Database_Operation_Exception('DELETE_ALL', $query, array(), $table, $e->getMessage()); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Deletes the rows in a given dataset using primary key columns. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Delete extends PHPUnit_Extensions_Database_Operation_RowBased +{ + protected $operationName = 'DELETE'; + + protected $iteratorDirection = self::ITERATOR_TYPE_REVERSE; + + protected function buildOperationQuery(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, PHPUnit_Extensions_Database_DB_IDatabaseConnection $connection) + { + $keys = $databaseTableMetaData->getPrimaryKeys(); + + $whereStatement = 'WHERE ' . implode(' AND ', $this->buildPreparedColumnArray($keys, $connection)); + + $query = " + DELETE FROM {$connection->quoteSchemaObject($table->getTableMetaData()->getTableName())} + {$whereStatement} + "; + + return $query; + } + + protected function buildOperationArguments(PHPUnit_Extensions_Database_DataSet_ITableMetaData $databaseTableMetaData, PHPUnit_Extensions_Database_DataSet_ITable $table, $row) + { + $args = array(); + foreach ($databaseTableMetaData->getPrimaryKeys() as $columnName) { + $args[] = $table->getValue($row, $columnName); + } + + return $args; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Thrown for exceptions encountered with database operations. Provides + * information regarding which operations failed and the query (if any) it + * failed on. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Operation_Exception extends RuntimeException +{ + /** + * @var string + */ + protected $operation; + + /** + * @var string + */ + protected $preparedQuery; + + /** + * @var array + */ + protected $preparedArgs; + + /** + * @var PHPUnit_Extensions_Database_DataSet_ITable + */ + protected $table; + + /** + * @var string + */ + protected $error; + + /** + * Creates a new dbunit operation exception + * + * @param string $operation + * @param string $current_query + * @param PHPUnit_Extensions_Database_DataSet_ITable $current_table + * @param string $error + */ + public function __construct($operation, $current_query, $current_args, $current_table, $error) + { + parent::__construct("{$operation} operation failed on query: {$current_query} using args: " . print_r($current_args, TRUE) . " [{$error}]"); + + $this->operation = $operation; + $this->preparedQuery = $current_query; + $this->preparedArgs = $current_args; + $this->table = $current_table; + $this->error = $error; + } + + public function getOperation() + { + return $this->operation; + } + + public function getQuery() + { + return $this->preparedQuery; + } + + public function getTable() + { + return $this->table; + } + + public function getArgs() + { + return $this->preparedArgs; + } + + public function getError() + { + return $this->error; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Thrown for exceptions encountered with database operations. Provides + * information regarding which operations failed and the query (if any) it + * failed on. + * + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_Database_Exception extends Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + */ +class Line +{ + const ADDED = 1; + const REMOVED = 2; + const UNCHANGED = 3; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $content; + + /** + * @param int $type + * @param string $content + */ + public function __construct($type = self::UNCHANGED, $content = '') + { + $this->type = $type; + $this->content = $content; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @return int + */ + public function getType() + { + return $this->type; + } +} +Diff + +Copyright (c) 2002-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + */ +class Chunk +{ + /** + * @var int + */ + private $start; + + /** + * @var int + */ + private $startRange; + + /** + * @var int + */ + private $end; + /** + * @var int + */ + private $endRange; + + /** + * @var array + */ + private $lines; + + /** + * @param int $start + * @param int $startRange + * @param int $end + * @param int $endRange + * @param array $lines + */ + public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = array()) + { + $this->start = (int) $start; + $this->startRange = (int) $startRange; + $this->end = (int) $end; + $this->endRange = (int) $endRange; + $this->lines = $lines; + } + + /** + * @return int + */ + public function getStart() + { + return $this->start; + } + + /** + * @return int + */ + public function getStartRange() + { + return $this->startRange; + } + + /** + * @return int + */ + public function getEnd() + { + return $this->end; + } + + /** + * @return int + */ + public function getEndRange() + { + return $this->endRange; + } + + /** + * @return array + */ + public function getLines() + { + return $this->lines; + } + + /** + * @param array $lines + */ + public function setLines(array $lines) + { + $this->lines = $lines; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + */ +class Diff +{ + /** + * @var string + */ + private $from; + + /** + * @var string + */ + private $to; + + /** + * @var Chunk[] + */ + private $chunks; + + /** + * @param string $from + * @param string $to + * @param Chunk[] $chunks + */ + public function __construct($from, $to, array $chunks = array()) + { + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * @return string + */ + public function getTo() + { + return $this->to; + } + + /** + * @return Chunk[] + */ + public function getChunks() + { + return $this->chunks; + } + + /** + * @param Chunk[] $chunks + */ + public function setChunks(array $chunks) + { + $this->chunks = $chunks; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +use SebastianBergmann\Diff\LCS\LongestCommonSubsequence; +use SebastianBergmann\Diff\LCS\TimeEfficientImplementation; +use SebastianBergmann\Diff\LCS\MemoryEfficientImplementation; + +/** + * Diff implementation. + */ +class Differ +{ + /** + * @var string + */ + private $header; + + /** + * @var bool + */ + private $showNonDiffLines; + + /** + * @param string $header + */ + public function __construct($header = "--- Original\n+++ New\n", $showNonDiffLines = true) + { + $this->header = $header; + $this->showNonDiffLines = $showNonDiffLines; + } + + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * + * @return string + */ + public function diff($from, $to, LongestCommonSubsequence $lcs = null) + { + if (!is_array($from) && !is_string($from)) { + $from = (string) $from; + } + + if (!is_array($to) && !is_string($to)) { + $to = (string) $to; + } + + $buffer = $this->header; + $diff = $this->diffToArray($from, $to, $lcs); + + $inOld = false; + $i = 0; + $old = array(); + + foreach ($diff as $line) { + if ($line[1] === 0 /* OLD */) { + if ($inOld === false) { + $inOld = $i; + } + } elseif ($inOld !== false) { + if (($i - $inOld) > 5) { + $old[$inOld] = $i - 1; + } + + $inOld = false; + } + + ++$i; + } + + $start = isset($old[0]) ? $old[0] : 0; + $end = count($diff); + + if ($tmp = array_search($end, $old)) { + $end = $tmp; + } + + $newChunk = true; + + for ($i = $start; $i < $end; $i++) { + if (isset($old[$i])) { + $buffer .= "\n"; + $newChunk = true; + $i = $old[$i]; + } + + if ($newChunk) { + if ($this->showNonDiffLines === true) { + $buffer .= "@@ @@\n"; + } + $newChunk = false; + } + + if ($diff[$i][1] === 1 /* ADDED */) { + $buffer .= '+' . $diff[$i][0] . "\n"; + } elseif ($diff[$i][1] === 2 /* REMOVED */) { + $buffer .= '-' . $diff[$i][0] . "\n"; + } elseif ($this->showNonDiffLines === true) { + $buffer .= ' ' . $diff[$i][0] . "\n"; + } + } + + return $buffer; + } + + /** + * Returns the diff between two arrays or strings as array. + * + * Each array element contains two elements: + * - [0] => string $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * + * @return array + */ + public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) + { + preg_match_all('(\r\n|\r|\n)', $from, $fromMatches); + preg_match_all('(\r\n|\r|\n)', $to, $toMatches); + + if (is_string($from)) { + $from = preg_split('(\r\n|\r|\n)', $from); + } + + if (is_string($to)) { + $to = preg_split('(\r\n|\r|\n)', $to); + } + + $start = array(); + $end = array(); + $fromLength = count($from); + $toLength = count($to); + $length = min($fromLength, $toLength); + + for ($i = 0; $i < $length; ++$i) { + if ($from[$i] === $to[$i]) { + $start[] = $from[$i]; + unset($from[$i], $to[$i]); + } else { + break; + } + } + + $length -= $i; + + for ($i = 1; $i < $length; ++$i) { + if ($from[$fromLength - $i] === $to[$toLength - $i]) { + array_unshift($end, $from[$fromLength - $i]); + unset($from[$fromLength - $i], $to[$toLength - $i]); + } else { + break; + } + } + + if ($lcs === null) { + $lcs = $this->selectLcsImplementation($from, $to); + } + + $common = $lcs->calculate(array_values($from), array_values($to)); + $diff = array(); + + if (isset($fromMatches[0]) && $toMatches[0] && + count($fromMatches[0]) === count($toMatches[0]) && + $fromMatches[0] !== $toMatches[0]) { + $diff[] = array( + '#Warning: Strings contain different line endings!', 0 + ); + } + + foreach ($start as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + reset($from); + reset($to); + + foreach ($common as $token) { + while ((($fromToken = reset($from)) !== $token)) { + $diff[] = array(array_shift($from), 2 /* REMOVED */); + } + + while ((($toToken = reset($to)) !== $token)) { + $diff[] = array(array_shift($to), 1 /* ADDED */); + } + + $diff[] = array($token, 0 /* OLD */); + + array_shift($from); + array_shift($to); + } + + while (($token = array_shift($from)) !== null) { + $diff[] = array($token, 2 /* REMOVED */); + } + + while (($token = array_shift($to)) !== null) { + $diff[] = array($token, 1 /* ADDED */); + } + + foreach ($end as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + return $diff; + } + + /** + * @param array $from + * @param array $to + * + * @return LongestCommonSubsequence + */ + private function selectLcsImplementation(array $from, array $to) + { + // We do not want to use the time-efficient implementation if its memory + // footprint will probably exceed this value. Note that the footprint + // calculation is only an estimation for the matrix and the LCS method + // will typically allocate a bit more memory than this. + $memoryLimit = 100 * 1024 * 1024; + + if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { + return new MemoryEfficientImplementation; + } + + return new TimeEfficientImplementation; + } + + /** + * Calculates the estimated memory footprint for the DP-based method. + * + * @param array $from + * @param array $to + * + * @return int + */ + private function calculateEstimatedFootprint(array $from, array $to) + { + $itemSize = PHP_INT_SIZE == 4 ? 76 : 144; + + return $itemSize * pow(min(count($from), count($to)), 2); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Memory-efficient implementation of longest common subsequence calculation. + */ +class MemoryEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to) + { + $cFrom = count($from); + $cTo = count($to); + + if ($cFrom == 0) { + return array(); + } elseif ($cFrom == 1) { + if (in_array($from[0], $to)) { + return array($from[0]); + } else { + return array(); + } + } else { + $i = intval($cFrom / 2); + $fromStart = array_slice($from, 0, $i); + $fromEnd = array_slice($from, $i); + $llB = $this->length($fromStart, $to); + $llE = $this->length(array_reverse($fromEnd), array_reverse($to)); + $jMax = 0; + $max = 0; + + for ($j = 0; $j <= $cTo; $j++) { + $m = $llB[$j] + $llE[$cTo - $j]; + + if ($m >= $max) { + $max = $m; + $jMax = $j; + } + } + + $toStart = array_slice($to, 0, $jMax); + $toEnd = array_slice($to, $jMax); + + return array_merge( + $this->calculate($fromStart, $toStart), + $this->calculate($fromEnd, $toEnd) + ); + } + } + + /** + * @param array $from + * @param array $to + * + * @return array + */ + private function length(array $from, array $to) + { + $current = array_fill(0, count($to) + 1, 0); + $cFrom = count($from); + $cTo = count($to); + + for ($i = 0; $i < $cFrom; $i++) { + $prev = $current; + + for ($j = 0; $j < $cTo; $j++) { + if ($from[$i] == $to[$j]) { + $current[$j + 1] = $prev[$j] + 1; + } else { + $current[$j + 1] = max($current[$j], $prev[$j + 1]); + } + } + } + + return $current; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Time-efficient implementation of longest common subsequence calculation. + */ +class TimeEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to) + { + $common = array(); + $fromLength = count($from); + $toLength = count($to); + $width = $fromLength + 1; + $matrix = new \SplFixedArray($width * ($toLength + 1)); + + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i] = 0; + } + + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[$j * $width] = 0; + } + + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $o = ($j * $width) + $i; + $matrix[$o] = max( + $matrix[$o - 1], + $matrix[$o - $width], + $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 + ); + } + } + + $i = $fromLength; + $j = $toLength; + + while ($i > 0 && $j > 0) { + if ($from[$i-1] === $to[$j-1]) { + $common[] = $from[$i-1]; + --$i; + --$j; + } else { + $o = ($j * $width) + $i; + if ($matrix[$o - $width] > $matrix[$o - 1]) { + --$j; + } else { + --$i; + } + } + } + + return array_reverse($common); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Interface for implementations of longest common subsequence calculation. + */ +interface LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + * Unified diff parser. + */ +class Parser +{ + /** + * @param string $string + * + * @return Diff[] + */ + public function parse($string) + { + $lines = preg_split('(\r\n|\r|\n)', $string); + $lineCount = count($lines); + $diffs = array(); + $diff = null; + $collected = array(); + + for ($i = 0; $i < $lineCount; ++$i) { + if (preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && + preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + $collected = array(); + } + + $diff = new Diff($fromMatch['file'], $toMatch['file']); + ++$i; + } else { + if (preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { + continue; + } + $collected[] = $lines[$i]; + } + } + + if (count($collected) && ($diff !== null)) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + } + + return $diffs; + } + + /** + * @param Diff $diff + * @param array $lines + */ + private function parseFileDiff(Diff $diff, array $lines) + { + $chunks = array(); + + foreach ($lines as $line) { + if (preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { + $chunk = new Chunk( + $match['start'], + isset($match['startrange']) ? max(1, $match['startrange']) : 1, + $match['end'], + isset($match['endrange']) ? max(1, $match['endrange']) : 1 + ); + + $chunks[] = $chunk; + $diffLines = array(); + continue; + } + + if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + + if ($match['type'] == '+') { + $type = Line::ADDED; + } elseif ($match['type'] == '-') { + $type = Line::REMOVED; + } + + $diffLines[] = new Line($type, $match['line']); + + if (isset($chunk)) { + $chunk->setLines($diffLines); + } + } + } + + $diff->setChunks($chunks); + } +} +Environment + +Copyright (c) 2014-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Environment; + +/** + * Utility class for HHVM/PHP environment handling. + */ +class Runtime +{ + /** + * @var string + */ + private static $binary; + + /** + * Returns true when Xdebug is supported or + * the runtime used is PHPDBG (PHP >= 7.0). + * + * @return bool + */ + public function canCollectCodeCoverage() + { + return $this->hasXdebug() || $this->hasPHPDBGCodeCoverage(); + } + + /** + * Returns the path to the binary of the current runtime. + * Appends ' --php' to the path when the runtime is HHVM. + * + * @return string + */ + public function getBinary() + { + // HHVM + if (self::$binary === null && $this->isHHVM()) { + if ((self::$binary = getenv('PHP_BINARY')) === false) { + self::$binary = PHP_BINARY; + } + + self::$binary = escapeshellarg(self::$binary) . ' --php'; + } + + // PHP >= 5.4.0 + if (self::$binary === null && defined('PHP_BINARY')) { + self::$binary = escapeshellarg(PHP_BINARY); + } + + // PHP < 5.4.0 + if (self::$binary === null) { + if (PHP_SAPI == 'cli' && isset($_SERVER['_'])) { + if (strpos($_SERVER['_'], 'phpunit') !== false) { + $file = file($_SERVER['_']); + + if (strpos($file[0], ' ') !== false) { + $tmp = explode(' ', $file[0]); + self::$binary = escapeshellarg(trim($tmp[1])); + } else { + self::$binary = escapeshellarg(ltrim(trim($file[0]), '#!')); + } + } elseif (strpos(basename($_SERVER['_']), 'php') !== false) { + self::$binary = escapeshellarg($_SERVER['_']); + } + } + } + + if (self::$binary === null) { + $possibleBinaryLocations = array( + PHP_BINDIR . '/php', + PHP_BINDIR . '/php-cli.exe', + PHP_BINDIR . '/php.exe' + ); + + foreach ($possibleBinaryLocations as $binary) { + if (is_readable($binary)) { + self::$binary = escapeshellarg($binary); + break; + } + } + } + + if (self::$binary === null) { + self::$binary = 'php'; + } + + return self::$binary; + } + + /** + * @return string + */ + public function getNameWithVersion() + { + return $this->getName() . ' ' . $this->getVersion(); + } + + /** + * @return string + */ + public function getName() + { + if ($this->isHHVM()) { + return 'HHVM'; + } elseif ($this->isPHPDBG()) { + return 'PHPDBG'; + } else { + return 'PHP'; + } + } + + /** + * @return string + */ + public function getVendorUrl() + { + if ($this->isHHVM()) { + return 'http://hhvm.com/'; + } else { + return 'https://secure.php.net/'; + } + } + + /** + * @return string + */ + public function getVersion() + { + if ($this->isHHVM()) { + return HHVM_VERSION; + } else { + return PHP_VERSION; + } + } + + /** + * Returns true when the runtime used is PHP and Xdebug is loaded. + * + * @return bool + */ + public function hasXdebug() + { + return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug'); + } + + /** + * Returns true when the runtime used is HHVM. + * + * @return bool + */ + public function isHHVM() + { + return defined('HHVM_VERSION'); + } + + /** + * Returns true when the runtime used is PHP without the PHPDBG SAPI. + * + * @return bool + */ + public function isPHP() + { + return !$this->isHHVM() && !$this->isPHPDBG(); + } + + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI. + * + * @return bool + */ + public function isPHPDBG() + { + return PHP_SAPI === 'phpdbg' && !$this->isHHVM(); + } + + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI + * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). + * + * @return bool + */ + public function hasPHPDBGCodeCoverage() + { + return $this->isPHPDBG() && function_exists('phpdbg_start_oplog'); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Environment; + +/** + */ +class Console +{ + const STDIN = 0; + const STDOUT = 1; + const STDERR = 2; + + /** + * Returns true if STDOUT supports colorization. + * + * This code has been copied and adapted from + * Symfony\Component\Console\Output\OutputStream. + * + * @return bool + */ + public function hasColorSupport() + { + if (DIRECTORY_SEPARATOR == '\\') { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + } + + if (!defined('STDOUT')) { + return false; + } + + return $this->isInteractive(STDOUT); + } + + /** + * Returns the number of columns of the terminal. + * + * @return int + */ + public function getNumberOfColumns() + { + if (DIRECTORY_SEPARATOR == '\\') { + $columns = 80; + + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + $columns = $matches[1]; + } elseif (function_exists('proc_open')) { + $process = proc_open( + 'mode CON', + array( + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ), + $pipes, + null, + null, + array('suppress_errors' => true) + ); + + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + $columns = $matches[2]; + } + } + } + + return $columns - 1; + } + + if (!$this->isInteractive(self::STDIN)) { + return 80; + } + + if (preg_match('#\d+ (\d+)#', shell_exec('stty size'), $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; + } + } + + if (preg_match('#columns = (\d+);#', shell_exec('stty'), $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; + } + } + + return 80; + } + + /** + * Returns if the file descriptor is an interactive terminal or not. + * + * @param int|resource $fileDescriptor + * + * @return bool + */ + public function isInteractive($fileDescriptor = self::STDOUT) + { + return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); + } +} +GlobalState + +Copyright (c) 2001-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +/** + * Exports parts of a Snapshot as PHP code. + */ +class CodeExporter +{ + /** + * @param Snapshot $snapshot + * @return string + */ + public function constants(Snapshot $snapshot) + { + $result = ''; + + foreach ($snapshot->constants() as $name => $value) { + $result .= sprintf( + 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", + $name, + $name, + $this->exportVariable($value) + ); + } + + return $result; + } + + /** + * @param Snapshot $snapshot + * @return string + */ + public function iniSettings(Snapshot $snapshot) + { + $result = ''; + + foreach ($snapshot->iniSettings() as $key => $value) { + $result .= sprintf( + '@ini_set(%s, %s);' . "\n", + $this->exportVariable($key), + $this->exportVariable($value) + ); + } + + return $result; + } + + /** + * @param mixed $variable + * @return string + */ + private function exportVariable($variable) + { + if (is_scalar($variable) || is_null($variable) || + (is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { + return var_export($variable, true); + } + + return 'unserialize(' . var_export(serialize($variable), true) . ')'; + } + + /** + * @param array $array + * @return bool + */ + private function arrayOnlyContainsScalars(array $array) + { + $result = true; + + foreach ($array as $element) { + if (is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!is_scalar($element) && !is_null($element)) { + $result = false; + } + + if ($result === false) { + break; + } + } + + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionProperty; + +/** + * Restorer of snapshots of global state. + */ +class Restorer +{ + /** + * Deletes function definitions that are not defined in a snapshot. + * + * @param Snapshot $snapshot + * @throws RuntimeException when the uopz_delete() function is not available + * @see https://github.com/krakjoe/uopz + */ + public function restoreFunctions(Snapshot $snapshot) + { + if (!function_exists('uopz_delete')) { + throw new RuntimeException('The uopz_delete() function is required for this operation'); + } + + $functions = get_defined_functions(); + + foreach (array_diff($functions['user'], $snapshot->functions()) as $function) { + uopz_delete($function); + } + } + + /** + * Restores all global and super-global variables from a snapshot. + * + * @param Snapshot $snapshot + */ + public function restoreGlobalVariables(Snapshot $snapshot) + { + $superGlobalArrays = $snapshot->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); + } + + $globalVariables = $snapshot->globalVariables(); + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) { + if (isset($globalVariables[$key])) { + $GLOBALS[$key] = $globalVariables[$key]; + } else { + unset($GLOBALS[$key]); + } + } + } + } + + /** + * Restores all static attributes in user-defined classes from this snapshot. + * + * @param Snapshot $snapshot + */ + public function restoreStaticAttributes(Snapshot $snapshot) + { + $current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false); + $newClasses = array_diff($current->classes(), $snapshot->classes()); + unset($current); + + foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { + foreach ($staticAttributes as $name => $value) { + $reflector = new ReflectionProperty($className, $name); + $reflector->setAccessible(true); + $reflector->setValue($value); + } + } + + foreach ($newClasses as $className) { + $class = new \ReflectionClass($className); + $defaults = $class->getDefaultProperties(); + + foreach ($class->getProperties() as $attribute) { + if (!$attribute->isStatic()) { + continue; + } + + $name = $attribute->getName(); + + if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) { + continue; + } + + if (!isset($defaults[$name])) { + continue; + } + + $attribute->setAccessible(true); + $attribute->setValue($defaults[$name]); + } + } + } + + /** + * Restores a super-global variable array from this snapshot. + * + * @param Snapshot $snapshot + * @param $superGlobalArray + */ + private function restoreSuperGlobalArray(Snapshot $snapshot, $superGlobalArray) + { + $superGlobalVariables = $snapshot->superGlobalVariables(); + + if (isset($GLOBALS[$superGlobalArray]) && + is_array($GLOBALS[$superGlobalArray]) && + isset($superGlobalVariables[$superGlobalArray])) { + $keys = array_keys( + array_merge( + $GLOBALS[$superGlobalArray], + $superGlobalVariables[$superGlobalArray] + ) + ); + + foreach ($keys as $key) { + if (isset($superGlobalVariables[$superGlobalArray][$key])) { + $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; + } else { + unset($GLOBALS[$superGlobalArray][$key]); + } + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +/** + */ +class RuntimeException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionClass; + +/** + * A blacklist for global state elements that should not be snapshotted. + */ +class Blacklist +{ + /** + * @var array + */ + private $globalVariables = array(); + + /** + * @var array + */ + private $classes = array(); + + /** + * @var array + */ + private $classNamePrefixes = array(); + + /** + * @var array + */ + private $parentClasses = array(); + + /** + * @var array + */ + private $interfaces = array(); + + /** + * @var array + */ + private $staticAttributes = array(); + + /** + * @param string $variableName + */ + public function addGlobalVariable($variableName) + { + $this->globalVariables[$variableName] = true; + } + + /** + * @param string $className + */ + public function addClass($className) + { + $this->classes[] = $className; + } + + /** + * @param string $className + */ + public function addSubclassesOf($className) + { + $this->parentClasses[] = $className; + } + + /** + * @param string $interfaceName + */ + public function addImplementorsOf($interfaceName) + { + $this->interfaces[] = $interfaceName; + } + + /** + * @param string $classNamePrefix + */ + public function addClassNamePrefix($classNamePrefix) + { + $this->classNamePrefixes[] = $classNamePrefix; + } + + /** + * @param string $className + * @param string $attributeName + */ + public function addStaticAttribute($className, $attributeName) + { + if (!isset($this->staticAttributes[$className])) { + $this->staticAttributes[$className] = array(); + } + + $this->staticAttributes[$className][$attributeName] = true; + } + + /** + * @param string $variableName + * @return bool + */ + public function isGlobalVariableBlacklisted($variableName) + { + return isset($this->globalVariables[$variableName]); + } + + /** + * @param string $className + * @param string $attributeName + * @return bool + */ + public function isStaticAttributeBlacklisted($className, $attributeName) + { + if (in_array($className, $this->classes)) { + return true; + } + + foreach ($this->classNamePrefixes as $prefix) { + if (strpos($className, $prefix) === 0) { + return true; + } + } + + $class = new ReflectionClass($className); + + foreach ($this->parentClasses as $type) { + if ($class->isSubclassOf($type)) { + return true; + } + } + + foreach ($this->interfaces as $type) { + if ($class->implementsInterface($type)) { + return true; + } + } + + if (isset($this->staticAttributes[$className][$attributeName])) { + return true; + } + + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionClass; +use Serializable; + +/** + * A snapshot of global state. + */ +class Snapshot +{ + /** + * @var Blacklist + */ + private $blacklist; + + /** + * @var array + */ + private $globalVariables = array(); + + /** + * @var array + */ + private $superGlobalArrays = array(); + + /** + * @var array + */ + private $superGlobalVariables = array(); + + /** + * @var array + */ + private $staticAttributes = array(); + + /** + * @var array + */ + private $iniSettings = array(); + + /** + * @var array + */ + private $includedFiles = array(); + + /** + * @var array + */ + private $constants = array(); + + /** + * @var array + */ + private $functions = array(); + + /** + * @var array + */ + private $interfaces = array(); + + /** + * @var array + */ + private $classes = array(); + + /** + * @var array + */ + private $traits = array(); + + /** + * Creates a snapshot of the current global state. + * + * @param Blacklist $blacklist + * @param bool $includeGlobalVariables + * @param bool $includeStaticAttributes + * @param bool $includeConstants + * @param bool $includeFunctions + * @param bool $includeClasses + * @param bool $includeInterfaces + * @param bool $includeTraits + * @param bool $includeIniSettings + * @param bool $includeIncludedFiles + */ + public function __construct(Blacklist $blacklist = null, $includeGlobalVariables = true, $includeStaticAttributes = true, $includeConstants = true, $includeFunctions = true, $includeClasses = true, $includeInterfaces = true, $includeTraits = true, $includeIniSettings = true, $includeIncludedFiles = true) + { + if ($blacklist === null) { + $blacklist = new Blacklist; + } + + $this->blacklist = $blacklist; + + if ($includeConstants) { + $this->snapshotConstants(); + } + + if ($includeFunctions) { + $this->snapshotFunctions(); + } + + if ($includeClasses || $includeStaticAttributes) { + $this->snapshotClasses(); + } + + if ($includeInterfaces) { + $this->snapshotInterfaces(); + } + + if ($includeGlobalVariables) { + $this->setupSuperGlobalArrays(); + $this->snapshotGlobals(); + } + + if ($includeStaticAttributes) { + $this->snapshotStaticAttributes(); + } + + if ($includeIniSettings) { + $this->iniSettings = ini_get_all(null, false); + } + + if ($includeIncludedFiles) { + $this->includedFiles = get_included_files(); + } + + if (function_exists('get_declared_traits')) { + $this->traits = get_declared_traits(); + } + } + + /** + * @return Blacklist + */ + public function blacklist() + { + return $this->blacklist; + } + + /** + * @return array + */ + public function globalVariables() + { + return $this->globalVariables; + } + + /** + * @return array + */ + public function superGlobalVariables() + { + return $this->superGlobalVariables; + } + + /** + * Returns a list of all super-global variable arrays. + * + * @return array + */ + public function superGlobalArrays() + { + return $this->superGlobalArrays; + } + + /** + * @return array + */ + public function staticAttributes() + { + return $this->staticAttributes; + } + + /** + * @return array + */ + public function iniSettings() + { + return $this->iniSettings; + } + + /** + * @return array + */ + public function includedFiles() + { + return $this->includedFiles; + } + + /** + * @return array + */ + public function constants() + { + return $this->constants; + } + + /** + * @return array + */ + public function functions() + { + return $this->functions; + } + + /** + * @return array + */ + public function interfaces() + { + return $this->interfaces; + } + + /** + * @return array + */ + public function classes() + { + return $this->classes; + } + + /** + * @return array + */ + public function traits() + { + return $this->traits; + } + + /** + * Creates a snapshot user-defined constants. + */ + private function snapshotConstants() + { + $constants = get_defined_constants(true); + + if (isset($constants['user'])) { + $this->constants = $constants['user']; + } + } + + /** + * Creates a snapshot user-defined functions. + */ + private function snapshotFunctions() + { + $functions = get_defined_functions(); + + $this->functions = $functions['user']; + } + + /** + * Creates a snapshot user-defined classes. + */ + private function snapshotClasses() + { + foreach (array_reverse(get_declared_classes()) as $className) { + $class = new ReflectionClass($className); + + if (!$class->isUserDefined()) { + break; + } + + $this->classes[] = $className; + } + + $this->classes = array_reverse($this->classes); + } + + /** + * Creates a snapshot user-defined interfaces. + */ + private function snapshotInterfaces() + { + foreach (array_reverse(get_declared_interfaces()) as $interfaceName) { + $class = new ReflectionClass($interfaceName); + + if (!$class->isUserDefined()) { + break; + } + + $this->interfaces[] = $interfaceName; + } + + $this->interfaces = array_reverse($this->interfaces); + } + + /** + * Creates a snapshot of all global and super-global variables. + */ + private function snapshotGlobals() + { + $superGlobalArrays = $this->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->snapshotSuperGlobalArray($superGlobalArray); + } + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + $this->canBeSerialized($GLOBALS[$key]) && + !$this->blacklist->isGlobalVariableBlacklisted($key)) { + $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); + } + } + } + + /** + * Creates a snapshot a super-global variable array. + * + * @param $superGlobalArray + */ + private function snapshotSuperGlobalArray($superGlobalArray) + { + $this->superGlobalVariables[$superGlobalArray] = array(); + + if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { + foreach ($GLOBALS[$superGlobalArray] as $key => $value) { + $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value)); + } + } + } + + /** + * Creates a snapshot of all static attributes in user-defined classes. + */ + private function snapshotStaticAttributes() + { + foreach ($this->classes as $className) { + $class = new ReflectionClass($className); + $snapshot = array(); + + foreach ($class->getProperties() as $attribute) { + if ($attribute->isStatic()) { + $name = $attribute->getName(); + + if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { + continue; + } + + $attribute->setAccessible(true); + $value = $attribute->getValue(); + + if ($this->canBeSerialized($value)) { + $snapshot[$name] = unserialize(serialize($value)); + } + } + } + + if (!empty($snapshot)) { + $this->staticAttributes[$className] = $snapshot; + } + } + } + + /** + * Returns a list of all super-global variable arrays. + * + * @return array + */ + private function setupSuperGlobalArrays() + { + $this->superGlobalArrays = array( + '_ENV', + '_POST', + '_GET', + '_COOKIE', + '_SERVER', + '_FILES', + '_REQUEST' + ); + + if (ini_get('register_long_arrays') == '1') { + $this->superGlobalArrays = array_merge( + $this->superGlobalArrays, + array( + 'HTTP_ENV_VARS', + 'HTTP_POST_VARS', + 'HTTP_GET_VARS', + 'HTTP_COOKIE_VARS', + 'HTTP_SERVER_VARS', + 'HTTP_POST_FILES' + ) + ); + } + } + + /** + * @param mixed $variable + * @return bool + * @todo Implement this properly + */ + private function canBeSerialized($variable) + { + if (!is_object($variable)) { + return !is_resource($variable); + } + + if ($variable instanceof \stdClass) { + return true; + } + + $class = new ReflectionClass($variable); + + do { + if ($class->isInternal()) { + return $variable instanceof Serializable; + } + } while ($class = $class->getParentClass()); + + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +/** + */ +interface Exception +{ +} +phpunit/phpunit: 4.8.26 +doctrine/instantiator: 1.0.5 +phpdocumentor/reflection-docblock: 2.0.4 +phpspec/prophecy: v1.6.0 +phpunit/dbunit: 1.4.1 +phpunit/php-code-coverage: 2.2.4 +phpunit/php-file-iterator: 1.4.1 +phpunit/php-invoker: 1.1.4 +phpunit/php-text-template: 1.2.1 +phpunit/php-timer: 1.0.8 +phpunit/php-token-stream: 1.4.8 +phpunit/phpunit-mock-objects: 2.3.8 +phpunit/phpunit-selenium: 1.4.2 +sebastian/comparator: 1.2.0 +sebastian/diff: 1.4.1 +sebastian/environment: 1.3.6 +sebastian/exporter: 1.2.1 +sebastian/global-state: 1.1.1 +sebastian/recursion-context: 1.0.2 +sebastian/version: 1.0.6 +symfony/yaml: v2.6.13 + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares \SplObjectStorage instances for equality. + */ +class SplObjectStorageComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + foreach ($actual as $object) { + if (!$expected->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + + foreach ($expected as $object) { + if (!$actual->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares objects for equality. + */ +class ObjectComparator extends ArrayComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_object($expected) && is_object($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @param array $processed + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + if (get_class($actual) !== get_class($expected)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + sprintf( + '%s is not instance of expected class "%s".', + $this->exporter->export($actual), + get_class($expected) + ) + ); + } + + // don't compare twice to allow for cyclic dependencies + if (in_array(array($actual, $expected), $processed, true) || + in_array(array($expected, $actual), $processed, true)) { + return; + } + + $processed[] = array($actual, $expected); + + // don't compare objects if they are identical + // this helps to avoid the error "maximum function nesting level reached" + // CAUTION: this conditional clause is not tested + if ($actual !== $expected) { + try { + parent::assertEquals( + $this->toArray($expected), + $this->toArray($actual), + $delta, + $canonicalize, + $ignoreCase, + $processed + ); + } catch (ComparisonFailure $e) { + throw new ComparisonFailure( + $expected, + $actual, + // replace "Array" with "MyClass object" + substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5), + substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + return $this->exporter->toArray($object); + } +} +Comparator + +Copyright (c) 2002-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Factory for comparators which compare values for equality. + */ +class Factory +{ + /** + * @var Comparator[] + */ + private $comparators = array(); + + /** + * @var Factory + */ + private static $instance; + + /** + * Constructs a new factory. + */ + public function __construct() + { + $this->register(new TypeComparator); + $this->register(new ScalarComparator); + $this->register(new NumericComparator); + $this->register(new DoubleComparator); + $this->register(new ArrayComparator); + $this->register(new ResourceComparator); + $this->register(new ObjectComparator); + $this->register(new ExceptionComparator); + $this->register(new SplObjectStorageComparator); + $this->register(new DOMNodeComparator); + $this->register(new MockObjectComparator); + $this->register(new DateTimeComparator); + } + + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new self; + } + + return self::$instance; + } + + /** + * Returns the correct comparator for comparing two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return Comparator + */ + public function getComparatorFor($expected, $actual) + { + foreach ($this->comparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + } + + /** + * Registers a new comparator. + * + * This comparator will be returned by getInstance() if its accept() method + * returns TRUE for the compared values. It has higher priority than the + * existing comparators, meaning that its accept() method will be tested + * before those of the other comparators. + * + * @param Comparator $comparator The registered comparator + */ + public function register(Comparator $comparator) + { + array_unshift($this->comparators, $comparator); + + $comparator->setFactory($this); + } + + /** + * Unregisters a comparator. + * + * This comparator will no longer be returned by getInstance(). + * + * @param Comparator $comparator The unregistered comparator + */ + public function unregister(Comparator $comparator) + { + foreach ($this->comparators as $key => $_comparator) { + if ($comparator === $_comparator) { + unset($this->comparators[$key]); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares scalar or NULL values for equality. + */ +class ScalarComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + * @since Method available since Release 3.6.0 + */ + public function accepts($expected, $actual) + { + return ((is_scalar($expected) xor null === $expected) && + (is_scalar($actual) xor null === $actual)) + // allow comparison between strings and objects featuring __toString() + || (is_string($expected) && is_object($actual) && method_exists($actual, '__toString')) + || (is_object($expected) && method_exists($expected, '__toString') && is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $expectedToCompare = $expected; + $actualToCompare = $actual; + + // always compare as strings to avoid strange behaviour + // otherwise 0 == 'Foobar' + if (is_string($expected) || is_string($actual)) { + $expectedToCompare = (string) $expectedToCompare; + $actualToCompare = (string) $actualToCompare; + + if ($ignoreCase) { + $expectedToCompare = strtolower($expectedToCompare); + $actualToCompare = strtolower($actualToCompare); + } + } + + if ($expectedToCompare != $actualToCompare) { + if (is_string($expected) && is_string($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two strings are equal.' + ); + } + + throw new ComparisonFailure( + $expected, + $actual, + // no diff is required + '', + '', + false, + sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares Exception instances for equality. + */ +class ExceptionComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \Exception && $actual instanceof \Exception; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset( + $array['file'], + $array['line'], + $array['trace'], + $array['string'], + $array['xdebug_message'] + ); + + return $array; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares DateTimeInterface instances for equality. + */ +class DateTimeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) && + ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $delta = new \DateInterval(sprintf('PT%sS', abs($delta))); + + $expectedLower = clone $expected; + $expectedUpper = clone $expected; + + if ($actual < $expectedLower->sub($delta) || + $actual > $expectedUpper->add($delta)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->dateTimeToString($expected), + $this->dateTimeToString($actual), + false, + 'Failed asserting that two DateTime objects are equal.' + ); + } + } + + /** + * Returns an ISO 8601 formatted string representation of a datetime or + * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly + * initialized. + * + * @param \DateTimeInterface $datetime + * @return string + */ + protected function dateTimeToString($datetime) + { + $string = $datetime->format(\DateTime::ISO8601); + + return $string ? $string : 'Invalid DateTimeInterface object'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use DOMDocument; +use DOMNode; + +/** + * Compares DOMNode instances for equality. + */ +class DOMNodeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof DOMNode && $actual instanceof DOMNode; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $expectedAsString = $this->nodeToText($expected, true, $ignoreCase); + $actualAsString = $this->nodeToText($actual, true, $ignoreCase); + + if ($expectedAsString !== $actualAsString) { + if ($expected instanceof DOMDocument) { + $type = 'documents'; + } else { + $type = 'nodes'; + } + + throw new ComparisonFailure( + $expected, + $actual, + $expectedAsString, + $actualAsString, + false, + sprintf("Failed asserting that two DOM %s are equal.\n", $type) + ); + } + } + + /** + * Returns the normalized, whitespace-cleaned, and indented textual + * representation of a DOMNode. + * + * @param DOMNode $node + * @param bool $canonicalize + * @param bool $ignoreCase + * @return string + */ + private function nodeToText(DOMNode $node, $canonicalize, $ignoreCase) + { + if ($canonicalize) { + $document = new DOMDocument; + $document->loadXML($node->C14N()); + + $node = $document; + } + + if ($node instanceof DOMDocument) { + $document = $node; + } else { + $document = $node->ownerDocument; + } + + $document->formatOutput = true; + $document->normalizeDocument(); + + if ($node instanceof DOMDocument) { + $text = $node->saveXML(); + } else { + $text = $document->saveXML($node); + } + + if ($ignoreCase) { + $text = strtolower($text); + } + + return $text; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares resources for equality. + */ +class ResourceComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_resource($expected) && is_resource($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($actual != $expected) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares arrays for equality. + */ +class ArrayComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_array($expected) && is_array($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @param array $processed + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + if ($canonicalize) { + sort($expected); + sort($actual); + } + + $remaining = $actual; + $expString = $actString = "Array (\n"; + $equal = true; + + foreach ($expected as $key => $value) { + unset($remaining[$key]); + + if (!array_key_exists($key, $actual)) { + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + + continue; + } + + try { + $comparator = $this->factory->getComparatorFor($value, $actual[$key]); + $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); + + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($actual[$key]) + ); + } catch (ComparisonFailure $e) { + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getExpectedAsString() + ? $this->indent($e->getExpectedAsString()) + : $this->exporter->shortenedExport($e->getExpected()) + ); + + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getActualAsString() + ? $this->indent($e->getActualAsString()) + : $this->exporter->shortenedExport($e->getActual()) + ); + + $equal = false; + } + } + + foreach ($remaining as $key => $value) { + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + } + + $expString .= ')'; + $actString .= ')'; + + if (!$equal) { + throw new ComparisonFailure( + $expected, + $actual, + $expString, + $actString, + false, + 'Failed asserting that two arrays are equal.' + ); + } + } + + protected function indent($lines) + { + return trim(str_replace("\n", "\n ", $lines)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Diff\Differ; + +/** + * Thrown when an assertion for string equality failed. + */ +class ComparisonFailure extends \RuntimeException +{ + /** + * Expected value of the retrieval which does not match $actual. + * @var mixed + */ + protected $expected; + + /** + * Actually retrieved value which does not match $expected. + * @var mixed + */ + protected $actual; + + /** + * The string representation of the expected value + * @var string + */ + protected $expectedAsString; + + /** + * The string representation of the actual value + * @var string + */ + protected $actualAsString; + + /** + * @var bool + */ + protected $identical; + + /** + * Optional message which is placed in front of the first line + * returned by toString(). + * @var string + */ + protected $message; + + /** + * Initialises with the expected value and the actual value. + * + * @param mixed $expected Expected value retrieved. + * @param mixed $actual Actual value retrieved. + * @param string $expectedAsString + * @param string $actualAsString + * @param bool $identical + * @param string $message A string which is prefixed on all returned lines + * in the difference output. + */ + public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '') + { + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; + $this->message = $message; + } + + /** + * @return mixed + */ + public function getActual() + { + return $this->actual; + } + + /** + * @return mixed + */ + public function getExpected() + { + return $this->expected; + } + + /** + * @return string + */ + public function getActualAsString() + { + return $this->actualAsString; + } + + /** + * @return string + */ + public function getExpectedAsString() + { + return $this->expectedAsString; + } + + /** + * @return string + */ + public function getDiff() + { + if (!$this->actualAsString && !$this->expectedAsString) { + return ''; + } + + $differ = new Differ("\n--- Expected\n+++ Actual\n"); + + return $differ->diff($this->expectedAsString, $this->actualAsString); + } + + /** + * @return string + */ + public function toString() + { + return $this->message . $this->getDiff(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares values for type equality. + */ +class TypeComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return true; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (gettype($expected) != gettype($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + sprintf( + '%s does not match expected type "%s".', + $this->exporter->shortenedExport($actual), + gettype($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares doubles for equality. + */ +class DoubleComparator extends NumericComparator +{ + /** + * Smallest value available in PHP. + * + * @var float + */ + const EPSILON = 0.0000000001; + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return (is_double($expected) || is_double($actual)) && is_numeric($expected) && is_numeric($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($delta == 0) { + $delta = self::EPSILON; + } + + parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares PHPUnit_Framework_MockObject_MockObject instances for equality. + */ +class MockObjectComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \PHPUnit_Framework_MockObject_MockObject && $actual instanceof \PHPUnit_Framework_MockObject_MockObject; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset($array['__phpunit_invocationMocker']); + + return $array; + } +} + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares numerical values for equality. + */ +class NumericComparator extends ScalarComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + // all numerical values, but not if one of them is a double + // or both of them are strings + return is_numeric($expected) && is_numeric($actual) && + !(is_double($expected) || is_double($actual)) && + !(is_string($expected) && is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (is_infinite($actual) && is_infinite($expected)) { + return; + } + + if ((is_infinite($actual) xor is_infinite($expected)) || + (is_nan($actual) or is_nan($expected)) || + abs($actual - $expected) > $delta) { + throw new ComparisonFailure( + $expected, + $actual, + '', + '', + false, + sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Exporter\Exporter; + +/** + * Abstract base class for comparators which compare values for equality. + */ +abstract class Comparator +{ + /** + * @var Factory + */ + protected $factory; + + /** + * @var Exporter + */ + protected $exporter; + + public function __construct() + { + $this->exporter = new Exporter; + } + + /** + * @param Factory $factory + */ + public function setFactory(Factory $factory) + { + $this->factory = $factory; + } + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + abstract public function accepts($expected, $actual); + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false); +} +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Unescaper encapsulates unescaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + */ +class Unescaper +{ + // Parser and Inline assume UTF-8 encoding, so escaped Unicode characters + // must be converted to that encoding. + // @deprecated since 2.5, to be removed in 3.0 + const ENCODING = 'UTF-8'; + + // Regex fragment that matches an escaped character in a double quoted + // string. + const REGEX_ESCAPED_CHARACTER = "\\\\([0abt\tnvfre \\\"\\/\\\\N_LP]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})"; + + /** + * Unescapes a single quoted string. + * + * @param string $value A single quoted string. + * + * @return string The unescaped string. + */ + public function unescapeSingleQuotedString($value) + { + return str_replace('\'\'', '\'', $value); + } + + /** + * Unescapes a double quoted string. + * + * @param string $value A double quoted string. + * + * @return string The unescaped string. + */ + public function unescapeDoubleQuotedString($value) + { + $self = $this; + $callback = function ($match) use ($self) { + return $self->unescapeCharacter($match[0]); + }; + + // evaluate the string + return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); + } + + /** + * Unescapes a character that was found in a double-quoted string. + * + * @param string $value An escaped character + * + * @return string The unescaped character + */ + public function unescapeCharacter($value) + { + switch ($value{1}) { + case '0': + return "\x0"; + case 'a': + return "\x7"; + case 'b': + return "\x8"; + case 't': + return "\t"; + case "\t": + return "\t"; + case 'n': + return "\n"; + case 'v': + return "\xB"; + case 'f': + return "\xC"; + case 'r': + return "\r"; + case 'e': + return "\x1B"; + case ' ': + return ' '; + case '"': + return '"'; + case '/': + return '/'; + case '\\': + return '\\'; + case 'N': + // U+0085 NEXT LINE + return "\xC2\x85"; + case '_': + // U+00A0 NO-BREAK SPACE + return "\xC2\xA0"; + case 'L': + // U+2028 LINE SEPARATOR + return "\xE2\x80\xA8"; + case 'P': + // U+2029 PARAGRAPH SEPARATOR + return "\xE2\x80\xA9"; + case 'x': + return self::utf8chr(hexdec(substr($value, 2, 2))); + case 'u': + return self::utf8chr(hexdec(substr($value, 2, 4))); + case 'U': + return self::utf8chr(hexdec(substr($value, 2, 8))); + } + } + + /** + * Get the UTF-8 character for the given code point. + * + * @param int $c The unicode code point + * + * @return string The corresponding UTF-8 character + */ + private static function utf8chr($c) + { + if (0x80 > $c %= 0x200000) { + return chr($c); + } + if (0x800 > $c) { + return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); + } + if (0x10000 > $c) { + return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } + + return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Yaml offers convenience methods to load and dump YAML. + * + * @author Fabien Potencier + * + * @api + */ +class Yaml +{ + /** + * Parses YAML into a PHP array. + * + * The parse method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. + * + * Usage: + * + * $array = Yaml::parse('config.yml'); + * print_r($array); + * + * + * As this method accepts both plain strings and file names as an input, + * you must validate the input before calling this method. Passing a file + * as an input is a deprecated feature and will be removed in 3.0. + * + * Note: the ability to pass file names to the Yaml::parse method is deprecated since version 2.2 and will be removed in 3.0. Pass the YAML contents of the file instead. + * + * @param string $input Path to a YAML file or a string containing YAML + * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise + * @param bool $objectSupport True if object support is enabled, false otherwise + * + * @return array The YAML converted to a PHP array + * + * @throws ParseException If the YAML is not valid + * + * @api + */ + public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false) + { + // if input is a file, process it + $file = ''; + if (strpos($input, "\n") === false && is_file($input)) { + if (false === is_readable($input)) { + throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $input)); + } + + $file = $input; + $input = file_get_contents($file); + } + + $yaml = new Parser(); + + try { + return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport); + } catch (ParseException $e) { + if ($file) { + $e->setParsedFile($file); + } + + throw $e; + } + } + + /** + * Dumps a PHP array to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param int $inline The level where you switch to inline YAML + * @param int $indent The amount of spaces to use for indentation of nested nodes. + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string A YAML string representing the original PHP array + * + * @api + */ + public static function dump($array, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false) + { + $yaml = new Dumper(); + $yaml->setIndentation($indent); + + return $yaml->dump($array, $inline, 0, $exceptionOnInvalidType, $objectSupport); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during dumping. + * + * @author Fabien Potencier + * + * @api + */ +class DumpException extends RuntimeException +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Romain Neutron + * + * @api + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Fabien Potencier + * + * @api + */ +class ParseException extends RuntimeException +{ + private $parsedFile; + private $parsedLine; + private $snippet; + private $rawMessage; + + /** + * Constructor. + * + * @param string $message The error message + * @param int $parsedLine The line where the error occurred + * @param int $snippet The snippet of code near the problem + * @param string $parsedFile The file name where the error occurred + * @param \Exception $previous The previous exception + */ + public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) + { + $this->parsedFile = $parsedFile; + $this->parsedLine = $parsedLine; + $this->snippet = $snippet; + $this->rawMessage = $message; + + $this->updateRepr(); + + parent::__construct($this->message, 0, $previous); + } + + /** + * Gets the snippet of code near the error. + * + * @return string The snippet of code + */ + public function getSnippet() + { + return $this->snippet; + } + + /** + * Sets the snippet of code near the error. + * + * @param string $snippet The code snippet + */ + public function setSnippet($snippet) + { + $this->snippet = $snippet; + + $this->updateRepr(); + } + + /** + * Gets the filename where the error occurred. + * + * This method returns null if a string is parsed. + * + * @return string The filename + */ + public function getParsedFile() + { + return $this->parsedFile; + } + + /** + * Sets the filename where the error occurred. + * + * @param string $parsedFile The filename + */ + public function setParsedFile($parsedFile) + { + $this->parsedFile = $parsedFile; + + $this->updateRepr(); + } + + /** + * Gets the line where the error occurred. + * + * @return int The file line + */ + public function getParsedLine() + { + return $this->parsedLine; + } + + /** + * Sets the line where the error occurred. + * + * @param int $parsedLine The file line + */ + public function setParsedLine($parsedLine) + { + $this->parsedLine = $parsedLine; + + $this->updateRepr(); + } + + private function updateRepr() + { + $this->message = $this->rawMessage; + + $dot = false; + if ('.' === substr($this->message, -1)) { + $this->message = substr($this->message, 0, -1); + $dot = true; + } + + if (null !== $this->parsedFile) { + if (PHP_VERSION_ID >= 50400) { + $jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; + } else { + $jsonOptions = 0; + } + $this->message .= sprintf(' in %s', json_encode($this->parsedFile, $jsonOptions)); + } + + if ($this->parsedLine >= 0) { + $this->message .= sprintf(' at line %d', $this->parsedLine); + } + + if ($this->snippet) { + $this->message .= sprintf(' (near "%s")', $this->snippet); + } + + if ($dot) { + $this->message .= '.'; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Fabien Potencier + * + * @api + */ +interface ExceptionInterface +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Escaper encapsulates escaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + */ +class Escaper +{ + // Characters that would cause a dumped string to require double quoting. + const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; + + // Mapping arrays for escaping a double quoted string. The backslash is + // first to ensure proper escaping because str_replace operates iteratively + // on the input arrays. This ordering of the characters avoids the use of strtr, + // which performs more slowly. + private static $escapees = array('\\', '\\\\', '\\"', '"', + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", + "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", + "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", + "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", + "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",); + private static $escaped = array('\\\\', '\\"', '\\\\', '\\"', + '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', + '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', + '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', + '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', + '\\N', '\\_', '\\L', '\\P',); + + /** + * Determines if a PHP value would require double quoting in YAML. + * + * @param string $value A PHP value + * + * @return bool True if the value would require double quotes. + */ + public static function requiresDoubleQuoting($value) + { + return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); + } + + /** + * Escapes and surrounds a PHP value with double quotes. + * + * @param string $value A PHP value + * + * @return string The quoted, escaped string + */ + public static function escapeWithDoubleQuotes($value) + { + return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); + } + + /** + * Determines if a PHP value would require single quoting in YAML. + * + * @param string $value A PHP value + * + * @return bool True if the value would require single quotes. + */ + public static function requiresSingleQuoting($value) + { + // Determines if a PHP value is entirely composed of a value that would + // require single quoting in YAML. + if (in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) { + return true; + } + + // Determines if the PHP value contains any single characters that would + // cause it to require single quoting in YAML. + return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); + } + + /** + * Escapes and surrounds a PHP value with single quotes. + * + * @param string $value A PHP value + * + * @return string The quoted, escaped string + */ + public static function escapeWithSingleQuotes($value) + { + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Parser parses YAML strings to convert them to PHP arrays. + * + * @author Fabien Potencier + */ +class Parser +{ + const FOLDED_SCALAR_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; + + private $offset = 0; + private $lines = array(); + private $currentLineNb = -1; + private $currentLine = ''; + private $refs = array(); + + /** + * Constructor. + * + * @param int $offset The offset of YAML document (used for line numbers in error messages) + */ + public function __construct($offset = 0) + { + $this->offset = $offset; + } + + /** + * Parses a YAML string to a PHP value. + * + * @param string $value A YAML string + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * @param bool $objectForMap true if maps should return a stdClass instead of array() + * + * @return mixed A PHP value + * + * @throws ParseException If the YAML is not valid + */ + public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) + { + if (!preg_match('//u', $value)) { + throw new ParseException('The YAML value does not appear to be valid UTF-8.'); + } + $this->currentLineNb = -1; + $this->currentLine = ''; + $value = $this->cleanup($value); + $this->lines = explode("\n", $value); + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); + } + + $data = array(); + $context = null; + $allowOverwrite = false; + while ($this->moveToNextLine()) { + if ($this->isCurrentLineEmpty()) { + continue; + } + + // tab? + if ("\t" === $this->currentLine[0]) { + throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + $isRef = $mergeNode = false; + if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { + if ($context && 'mapping' == $context) { + throw new ParseException('You cannot define a sequence item when in a mapping'); + } + $context = 'sequence'; + + if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + // array + if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new self($c); + $parser->refs = &$this->refs; + $data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap); + } else { + if (isset($values['leadspaces']) + && preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches) + ) { + // this is a compact notation element, add to next block and parse + $c = $this->getRealCurrentLineNb(); + $parser = new self($c); + $parser->refs = &$this->refs; + + $block = $values['value']; + if ($this->isNextLineIndented()) { + $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1); + } + + $data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport, $objectForMap); + } else { + $data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap); + } + } + } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) { + if ($context && 'sequence' == $context) { + throw new ParseException('You cannot define a mapping item when in a sequence'); + } + $context = 'mapping'; + + // force correct settings + Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); + try { + $key = Inline::parseScalar($values['key']); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + if ('<<' === $key) { + $mergeNode = true; + $allowOverwrite = true; + if (isset($values['value']) && 0 === strpos($values['value'], '*')) { + $refName = substr($values['value'], 1); + if (!array_key_exists($refName, $this->refs)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + $refValue = $this->refs[$refName]; + + if (!is_array($refValue)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + foreach ($refValue as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } + } else { + if (isset($values['value']) && $values['value'] !== '') { + $value = $values['value']; + } else { + $value = $this->getNextEmbedBlock(); + } + $c = $this->getRealCurrentLineNb() + 1; + $parser = new self($c); + $parser->refs = &$this->refs; + $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap); + + if (!is_array($parsed)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + if (isset($parsed[0])) { + // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes + // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier + // in the sequence override keys specified in later mapping nodes. + foreach ($parsed as $parsedItem) { + if (!is_array($parsedItem)) { + throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); + } + + foreach ($parsedItem as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } + } + } else { + // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the + // current mapping, unless the key already exists in it. + foreach ($parsed as $key => $value) { + if (!isset($data[$key])) { + $data[$key] = $value; + } + } + } + } + } elseif (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + if ($mergeNode) { + // Merge keys + } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + // hash + // if next line is less indented or equal, then it means that the current value is null + if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + $data[$key] = null; + } + } else { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new self($c); + $parser->refs = &$this->refs; + $value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap); + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + $data[$key] = $value; + } + } + } else { + $value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap); + // Spec: Keys MUST be unique; first one wins. + // But overwriting is allowed when a merge node is used in current block. + if ($allowOverwrite || !isset($data[$key])) { + $data[$key] = $value; + } + } + } else { + // multiple documents are not supported + if ('---' === $this->currentLine) { + throw new ParseException('Multiple documents are not supported.'); + } + + // 1-liner optionally followed by newline(s) + if (is_string($value) && $this->lines[0] === trim($value)) { + try { + $value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + if (is_array($value)) { + $first = reset($value); + if (is_string($first) && 0 === strpos($first, '*')) { + $data = array(); + foreach ($value as $alias) { + $data[] = $this->refs[substr($alias, 1)]; + } + $value = $data; + } + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return $value; + } + + switch (preg_last_error()) { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error.'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached.'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached.'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data.'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; + break; + default: + $error = 'Unable to parse.'; + } + + throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + if ($isRef) { + $this->refs[$isRef] = end($data); + } + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return empty($data) ? null : $data; + } + + /** + * Returns the current line number (takes the offset into account). + * + * @return int The current line number + */ + private function getRealCurrentLineNb() + { + return $this->currentLineNb + $this->offset; + } + + /** + * Returns the current line indentation. + * + * @return int The current line indentation + */ + private function getCurrentLineIndentation() + { + return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); + } + + /** + * Returns the next embed block of YAML. + * + * @param int $indentation The indent level at which the block is to be read, or null for default + * @param bool $inSequence True if the enclosing data structure is a sequence + * + * @return string A YAML string + * + * @throws ParseException When indentation problem are detected + */ + private function getNextEmbedBlock($indentation = null, $inSequence = false) + { + $oldLineIndentation = $this->getCurrentLineIndentation(); + + if (!$this->moveToNextLine()) { + return; + } + + if (null === $indentation) { + $newIndent = $this->getCurrentLineIndentation(); + + $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem($this->currentLine); + + if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } else { + $newIndent = $indentation; + } + + $data = array(); + if ($this->getCurrentLineIndentation() >= $newIndent) { + $data[] = substr($this->currentLine, $newIndent); + } else { + $this->moveToPreviousLine(); + + return; + } + + if ($inSequence && $oldLineIndentation === $newIndent && '-' === $data[0][0]) { + // the previous line contained a dash but no item content, this line is a sequence item with the same indentation + // and therefore no nested list or mapping + $this->moveToPreviousLine(); + + return; + } + + $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem($this->currentLine); + + // Comments must not be removed inside a string block (ie. after a line ending with "|") + $removeCommentsPattern = '~'.self::FOLDED_SCALAR_PATTERN.'$~'; + $removeComments = !preg_match($removeCommentsPattern, $this->currentLine); + + while ($this->moveToNextLine()) { + $indent = $this->getCurrentLineIndentation(); + + if ($indent === $newIndent) { + $removeComments = !preg_match($removeCommentsPattern, $this->currentLine); + } + + if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem($this->currentLine) && $newIndent === $indent) { + $this->moveToPreviousLine(); + break; + } + + if ($this->isCurrentLineBlank()) { + $data[] = substr($this->currentLine, $newIndent); + continue; + } + + if ($removeComments && $this->isCurrentLineComment()) { + continue; + } + + if ($indent >= $newIndent) { + $data[] = substr($this->currentLine, $newIndent); + } elseif (0 == $indent) { + $this->moveToPreviousLine(); + + break; + } else { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } + + return implode("\n", $data); + } + + /** + * Moves the parser to the next line. + * + * @return bool + */ + private function moveToNextLine() + { + if ($this->currentLineNb >= count($this->lines) - 1) { + return false; + } + + $this->currentLine = $this->lines[++$this->currentLineNb]; + + return true; + } + + /** + * Moves the parser to the previous line. + */ + private function moveToPreviousLine() + { + $this->currentLine = $this->lines[--$this->currentLineNb]; + } + + /** + * Parses a YAML value. + * + * @param string $value A YAML value + * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise + * @param bool $objectSupport True if object support is enabled, false otherwise + * @param bool $objectForMap true if maps should return a stdClass instead of array() + * + * @return mixed A PHP value + * + * @throws ParseException When reference does not exist + */ + private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $objectForMap) + { + if (0 === strpos($value, '*')) { + if (false !== $pos = strpos($value, '#')) { + $value = substr($value, 1, $pos - 2); + } else { + $value = substr($value, 1); + } + + if (!array_key_exists($value, $this->refs)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine); + } + + return $this->refs[$value]; + } + + if (preg_match('/^'.self::FOLDED_SCALAR_PATTERN.'$/', $value, $matches)) { + $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; + + return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); + } + + try { + return Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + } + + /** + * Parses a folded scalar. + * + * @param string $separator The separator that was used to begin this folded scalar (| or >) + * @param string $indicator The indicator that was used to begin this folded scalar (+ or -) + * @param int $indentation The indentation that was used to begin this folded scalar + * + * @return string The text value + */ + private function parseFoldedScalar($separator, $indicator = '', $indentation = 0) + { + $notEOF = $this->moveToNextLine(); + if (!$notEOF) { + return ''; + } + + $isCurrentLineBlank = $this->isCurrentLineBlank(); + $text = ''; + + // leading blank lines are consumed before determining indentation + while ($notEOF && $isCurrentLineBlank) { + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $text .= "\n"; + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + + // determine indentation if not specified + if (0 === $indentation) { + if (preg_match('/^ +/', $this->currentLine, $matches)) { + $indentation = strlen($matches[0]); + } + } + + if ($indentation > 0) { + $pattern = sprintf('/^ {%d}(.*)$/', $indentation); + + while ( + $notEOF && ( + $isCurrentLineBlank || + preg_match($pattern, $this->currentLine, $matches) + ) + ) { + if ($isCurrentLineBlank) { + $text .= substr($this->currentLine, $indentation); + } else { + $text .= $matches[1]; + } + + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $text .= "\n"; + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + } elseif ($notEOF) { + $text .= "\n"; + } + + if ($notEOF) { + $this->moveToPreviousLine(); + } + + // replace all non-trailing single newlines with spaces in folded blocks + if ('>' === $separator) { + preg_match('/(\n*)$/', $text, $matches); + $text = preg_replace('/(?getCurrentLineIndentation(); + $EOF = !$this->moveToNextLine(); + + while (!$EOF && $this->isCurrentLineEmpty()) { + $EOF = !$this->moveToNextLine(); + } + + if ($EOF) { + return false; + } + + $ret = false; + if ($this->getCurrentLineIndentation() > $currentIndentation) { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the current line is blank or if it is a comment line. + * + * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise + */ + private function isCurrentLineEmpty() + { + return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); + } + + /** + * Returns true if the current line is blank. + * + * @return bool Returns true if the current line is blank, false otherwise + */ + private function isCurrentLineBlank() + { + return '' == trim($this->currentLine, ' '); + } + + /** + * Returns true if the current line is a comment line. + * + * @return bool Returns true if the current line is a comment line, false otherwise + */ + private function isCurrentLineComment() + { + //checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = ltrim($this->currentLine, ' '); + + return $ltrimmedLine[0] === '#'; + } + + /** + * Cleanups a YAML string to be parsed. + * + * @param string $value The input YAML string + * + * @return string A cleaned up YAML string + */ + private function cleanup($value) + { + $value = str_replace(array("\r\n", "\r"), "\n", $value); + + // strip YAML header + $count = 0; + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); + $this->offset += $count; + + // remove leading comments + $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); + if ($count == 1) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + } + + // remove start of the document marker (---) + $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); + if ($count == 1) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + + // remove end of the document marker (...) + $value = preg_replace('#\.\.\.\s*$#', '', $value); + } + + return $value; + } + + /** + * Returns true if the next line starts unindented collection. + * + * @return bool Returns true if the next line starts unindented collection, false otherwise + */ + private function isNextLineUnIndentedCollection() + { + $currentIndentation = $this->getCurrentLineIndentation(); + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineEmpty()) { + $notEOF = $this->moveToNextLine(); + } + + if (false === $notEOF) { + return false; + } + + $ret = false; + if ( + $this->getCurrentLineIndentation() == $currentIndentation + && + $this->isStringUnIndentedCollectionItem($this->currentLine) + ) { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the string is un-indented collection item. + * + * @return bool Returns true if the string is un-indented collection item, false otherwise + */ + private function isStringUnIndentedCollectionItem() + { + return (0 === strpos($this->currentLine, '- ')); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Exception\DumpException; + +/** + * Inline implements a YAML parser/dumper for the YAML inline syntax. + * + * @author Fabien Potencier + */ +class Inline +{ + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; + + private static $exceptionOnInvalidType = false; + private static $objectSupport = false; + private static $objectForMap = false; + + /** + * Converts a YAML string to a PHP array. + * + * @param string $value A YAML string + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * @param bool $objectForMap true if maps should return a stdClass instead of array() + * @param array $references Mapping of variable names to values + * + * @return array A PHP array representing the YAML string + * + * @throws ParseException + */ + public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false, $references = array()) + { + self::$exceptionOnInvalidType = $exceptionOnInvalidType; + self::$objectSupport = $objectSupport; + self::$objectForMap = $objectForMap; + + $value = trim($value); + + if ('' === $value) { + return ''; + } + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + $i = 0; + switch ($value[0]) { + case '[': + $result = self::parseSequence($value, $i, $references); + ++$i; + break; + case '{': + $result = self::parseMapping($value, $i, $references); + ++$i; + break; + default: + $result = self::parseScalar($value, null, array('"', "'"), $i, true, $references); + } + + // some comments are allowed at the end + if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return $result; + } + + /** + * Dumps a given PHP variable to a YAML string. + * + * @param mixed $value The PHP variable to convert + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string The YAML string representing the PHP array + * + * @throws DumpException When trying to dump PHP resource + */ + public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false) + { + switch (true) { + case is_resource($value): + if ($exceptionOnInvalidType) { + throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); + } + + return 'null'; + case is_object($value): + if ($objectSupport) { + return '!!php/object:'.serialize($value); + } + + if ($exceptionOnInvalidType) { + throw new DumpException('Object support when dumping a YAML file has been disabled.'); + } + + return 'null'; + case is_array($value): + return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport); + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case ctype_digit($value): + return is_string($value) ? "'$value'" : (int) $value; + case is_numeric($value): + $locale = setlocale(LC_NUMERIC, 0); + if (false !== $locale) { + setlocale(LC_NUMERIC, 'C'); + } + if (is_float($value)) { + $repr = (string) $value; + if (is_infinite($value)) { + $repr = str_ireplace('INF', '.Inf', $repr); + } elseif (floor($value) == $value && $repr == $value) { + // Preserve float data type since storing a whole number will result in integer value. + $repr = '!!float '.$repr; + } + } else { + $repr = is_string($value) ? "'$value'" : (string) $value; + } + if (false !== $locale) { + setlocale(LC_NUMERIC, $locale); + } + + return $repr; + case '' == $value: + return "''"; + case Escaper::requiresDoubleQuoting($value): + return Escaper::escapeWithDoubleQuotes($value); + case Escaper::requiresSingleQuoting($value): + case preg_match(self::getHexRegex(), $value): + case preg_match(self::getTimestampRegex(), $value): + return Escaper::escapeWithSingleQuotes($value); + default: + return $value; + } + } + + /** + * Dumps a PHP array to a YAML string. + * + * @param array $value The PHP array to dump + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string The YAML string representing the PHP array + */ + private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport) + { + // array + $keys = array_keys($value); + $keysCount = count($keys); + if ((1 === $keysCount && '0' == $keys[0]) + || ($keysCount > 1 && array_reduce($keys, function ($v, $w) { return (int) $v + $w; }, 0) === $keysCount * ($keysCount - 1) / 2) + ) { + $output = array(); + foreach ($value as $val) { + $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport); + } + + return sprintf('[%s]', implode(', ', $output)); + } + + // mapping + $output = array(); + foreach ($value as $key => $val) { + $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport)); + } + + return sprintf('{ %s }', implode(', ', $output)); + } + + /** + * Parses a scalar to a YAML string. + * + * @param string $scalar + * @param string $delimiters + * @param array $stringDelimiters + * @param int &$i + * @param bool $evaluate + * @param array $references + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array()) + { + if (in_array($scalar[$i], $stringDelimiters)) { + // quoted scalar + $output = self::parseQuotedScalar($scalar, $i); + + if (null !== $delimiters) { + $tmp = ltrim(substr($scalar, $i), ' '); + if (!in_array($tmp[0], $delimiters)) { + throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); + } + } + } else { + // "normal" string + if (!$delimiters) { + $output = substr($scalar, $i); + $i += strlen($output); + + // remove comments + if (false !== $strpos = strpos($output, ' #')) { + $output = rtrim(substr($output, 0, $strpos)); + } + } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { + $output = $match[1]; + $i += strlen($output); + } else { + throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar)); + } + + if ($evaluate) { + $output = self::evaluateScalar($output, $references); + } + } + + return $output; + } + + /** + * Parses a quoted scalar to YAML. + * + * @param string $scalar + * @param int &$i + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseQuotedScalar($scalar, &$i) + { + if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { + throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); + } + + $output = substr($match[0], 1, strlen($match[0]) - 2); + + $unescaper = new Unescaper(); + if ('"' == $scalar[$i]) { + $output = $unescaper->unescapeDoubleQuotedString($output); + } else { + $output = $unescaper->unescapeSingleQuotedString($output); + } + + $i += strlen($match[0]); + + return $output; + } + + /** + * Parses a sequence to a YAML string. + * + * @param string $sequence + * @param int &$i + * @param array $references + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseSequence($sequence, &$i = 0, $references = array()) + { + $output = array(); + $len = strlen($sequence); + ++$i; + + // [foo, bar, ...] + while ($i < $len) { + switch ($sequence[$i]) { + case '[': + // nested sequence + $output[] = self::parseSequence($sequence, $i, $references); + break; + case '{': + // nested mapping + $output[] = self::parseMapping($sequence, $i, $references); + break; + case ']': + return $output; + case ',': + case ' ': + break; + default: + $isQuoted = in_array($sequence[$i], array('"', "'")); + $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references); + + // the value can be an array if a reference has been resolved to an array var + if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) { + // embedded mapping? + try { + $pos = 0; + $value = self::parseMapping('{'.$value.'}', $pos, $references); + } catch (\InvalidArgumentException $e) { + // no, it's not + } + } + + $output[] = $value; + + --$i; + } + + ++$i; + } + + throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence)); + } + + /** + * Parses a mapping to a YAML string. + * + * @param string $mapping + * @param int &$i + * @param array $references + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseMapping($mapping, &$i = 0, $references = array()) + { + $output = array(); + $len = strlen($mapping); + ++$i; + + // {foo: bar, bar:foo, ...} + while ($i < $len) { + switch ($mapping[$i]) { + case ' ': + case ',': + ++$i; + continue 2; + case '}': + if (self::$objectForMap) { + return (object) $output; + } + + return $output; + } + + // key + $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); + + // value + $done = false; + + while ($i < $len) { + switch ($mapping[$i]) { + case '[': + // nested sequence + $value = self::parseSequence($mapping, $i, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } + $done = true; + break; + case '{': + // nested mapping + $value = self::parseMapping($mapping, $i, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } + $done = true; + break; + case ':': + case ' ': + break; + default: + $value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references); + // Spec: Keys MUST be unique; first one wins. + // Parser cannot abort this mapping earlier, since lines + // are processed sequentially. + if (!isset($output[$key])) { + $output[$key] = $value; + } + $done = true; + --$i; + } + + ++$i; + + if ($done) { + continue 2; + } + } + } + + throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping)); + } + + /** + * Evaluates scalars and replaces magic values. + * + * @param string $scalar + * @param array $references + * + * @return string A YAML string + * + * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved + */ + private static function evaluateScalar($scalar, $references = array()) + { + $scalar = trim($scalar); + $scalarLower = strtolower($scalar); + + if (0 === strpos($scalar, '*')) { + if (false !== $pos = strpos($scalar, '#')) { + $value = substr($scalar, 1, $pos - 2); + } else { + $value = substr($scalar, 1); + } + + // an unquoted * + if (false === $value || '' === $value) { + throw new ParseException('A reference must contain at least one character.'); + } + + if (!array_key_exists($value, $references)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $value)); + } + + return $references[$value]; + } + + switch (true) { + case 'null' === $scalarLower: + case '' === $scalar: + case '~' === $scalar: + return; + case 'true' === $scalarLower: + return true; + case 'false' === $scalarLower: + return false; + // Optimise for returning strings. + case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]): + switch (true) { + case 0 === strpos($scalar, '!str'): + return (string) substr($scalar, 5); + case 0 === strpos($scalar, '! '): + return (int) self::parseScalar(substr($scalar, 2)); + case 0 === strpos($scalar, '!!php/object:'): + if (self::$objectSupport) { + return unserialize(substr($scalar, 13)); + } + + if (self::$exceptionOnInvalidType) { + throw new ParseException('Object support when parsing a YAML file has been disabled.'); + } + + return; + case 0 === strpos($scalar, '!!float '): + return (float) substr($scalar, 8); + case ctype_digit($scalar): + $raw = $scalar; + $cast = (int) $scalar; + + return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); + case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): + $raw = $scalar; + $cast = (int) $scalar; + + return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); + case is_numeric($scalar): + case preg_match(self::getHexRegex(), $scalar): + return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; + case '.inf' === $scalarLower: + case '.nan' === $scalarLower: + return -log(0); + case '-.inf' === $scalarLower: + return log(0); + case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): + return (float) str_replace(',', '', $scalar); + case preg_match(self::getTimestampRegex(), $scalar): + return strtotime($scalar); + } + default: + return (string) $scalar; + } + } + + /** + * Gets a regex that matches a YAML date. + * + * @return string The regular expression + * + * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 + */ + private static function getTimestampRegex() + { + return <<[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[0-9][0-9]))?))?)? + $~x +EOF; + } + + /** + * Gets a regex that matches a YAML number in hexadecimal notation. + * + * @return string + */ + private static function getHexRegex() + { + return '~^0x[0-9a-f]++$~i'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Dumper dumps PHP variables to YAML strings. + * + * @author Fabien Potencier + */ +class Dumper +{ + /** + * The amount of spaces to use for indentation of nested nodes. + * + * @var int + */ + protected $indentation = 4; + + /** + * Sets the indentation. + * + * @param int $num The amount of spaces to use for indentation of nested nodes. + */ + public function setIndentation($num) + { + $this->indentation = (int) $num; + } + + /** + * Dumps a PHP value to YAML. + * + * @param mixed $input The PHP value + * @param int $inline The level where you switch to inline YAML + * @param int $indent The level of indentation (used internally) + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string The YAML representation of the PHP value + */ + public function dump($input, $inline = 0, $indent = 0, $exceptionOnInvalidType = false, $objectSupport = false) + { + $output = ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; + + if ($inline <= 0 || !is_array($input) || empty($input)) { + $output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport); + } else { + $isAHash = array_keys($input) !== range(0, count($input) - 1); + + foreach ($input as $key => $value) { + $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + + $output .= sprintf('%s%s%s%s', + $prefix, + $isAHash ? Inline::dump($key, $exceptionOnInvalidType, $objectSupport).':' : '-', + $willBeInlined ? ' ' : "\n", + $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $exceptionOnInvalidType, $objectSupport) + ).($willBeInlined ? "\n" : ''); + } + } + + return $output; + } +} +Recursion Context + +Copyright (c) 2002-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +final class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + * A context containing previously processed arrays and objects + * when recursively processing a value. + */ +final class Context +{ + /** + * @var array[] + */ + private $arrays; + + /** + * @var \SplObjectStorage + */ + private $objects; + + /** + * Initialises the context + */ + public function __construct() + { + $this->arrays = array(); + $this->objects = new \SplObjectStorage; + } + + /** + * Adds a value to the context. + * + * @param array|object $value The value to add. + * @return int|string The ID of the stored value, either as + * a string or integer. + * @throws InvalidArgumentException Thrown if $value is not an array or + * object + */ + public function add(&$value) + { + if (is_array($value)) { + return $this->addArray($value); + } + + else if (is_object($value)) { + return $this->addObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * Checks if the given value exists within the context. + * + * @param array|object $value The value to check. + * @return int|string|false The string or integer ID of the stored + * value if it has already been seen, or + * false if the value is not stored. + * @throws InvalidArgumentException Thrown if $value is not an array or + * object + */ + public function contains(&$value) + { + if (is_array($value)) { + return $this->containsArray($value); + } + + else if (is_object($value)) { + return $this->containsObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * @param array $array + * @return bool|int + */ + private function addArray(array &$array) + { + $key = $this->containsArray($array); + + if ($key !== false) { + return $key; + } + + $this->arrays[] = &$array; + + return count($this->arrays) - 1; + } + + /** + * @param object $object + * @return string + */ + private function addObject($object) + { + if (!$this->objects->contains($object)) { + $this->objects->attach($object); + } + + return spl_object_hash($object); + } + + /** + * @param array $array + * @return int|false + */ + private function containsArray(array &$array) + { + $keys = array_keys($this->arrays, $array, true); + $hash = '_Key_' . microtime(true); + + foreach ($keys as $key) { + $this->arrays[$key][$hash] = $hash; + + if (isset($array[$hash]) && $array[$hash] === $hash) { + unset($this->arrays[$key][$hash]); + + return $key; + } + + unset($this->arrays[$key][$hash]); + } + + return false; + } + + /** + * @param object $value + * @return string|false + */ + private function containsObject($value) + { + if ($this->objects->contains($value)) { + return spl_object_hash($value); + } + + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(ticks = 1); + +/** + * Utility class for invoking callables with a timeout. + * + * @since Class available since Release 1.0.0 + */ +class PHP_Invoker +{ + /** + * @var int + */ + protected $timeout; + + /** + * Invokes a callable and raises an exception when the execution does not + * finish before the specified timeout. + * + * @param callable $callable + * @param array $arguments + * @param int $timeout in seconds + * @return mixed + * @throws InvalidArgumentException + */ + public function invoke($callable, array $arguments, $timeout) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException; + } + + if (!is_integer($timeout)) { + throw new InvalidArgumentException; + } + + pcntl_signal(SIGALRM, array($this, 'callback'), TRUE); + pcntl_alarm($timeout); + + $this->timeout = $timeout; + + try { + $result = call_user_func_array($callable, $arguments); + } + + catch (Exception $e) { + pcntl_alarm(0); + throw $e; + } + + pcntl_alarm(0); + + return $result; + } + + /** + * Invoked by pcntl_signal() when a SIGALRM occurs. + */ + public function callback() + { + throw new PHP_Invoker_TimeoutException( + sprintf( + 'Execution aborted after %s', + PHP_Timer::secondsToTimeString($this->timeout) + ) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 1.0.0 + */ +class PHP_Invoker_TimeoutException extends RuntimeException +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestRunner for the Command Line Interface (CLI) + * PHP SAPI Module. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_TextUI_Command +{ + /** + * @var array + */ + protected $arguments = array( + 'listGroups' => false, + 'loader' => null, + 'useDefaultConfiguration' => true + ); + + /** + * @var array + */ + protected $options = array(); + + /** + * @var array + */ + protected $longOptions = array( + 'colors==' => null, + 'bootstrap=' => null, + 'columns=' => null, + 'configuration=' => null, + 'coverage-clover=' => null, + 'coverage-crap4j=' => null, + 'coverage-html=' => null, + 'coverage-php=' => null, + 'coverage-text==' => null, + 'coverage-xml=' => null, + 'debug' => null, + 'exclude-group=' => null, + 'filter=' => null, + 'testsuite=' => null, + 'group=' => null, + 'help' => null, + 'include-path=' => null, + 'list-groups' => null, + 'loader=' => null, + 'log-json=' => null, + 'log-junit=' => null, + 'log-tap=' => null, + 'process-isolation' => null, + 'repeat=' => null, + 'stderr' => null, + 'stop-on-error' => null, + 'stop-on-failure' => null, + 'stop-on-incomplete' => null, + 'stop-on-risky' => null, + 'stop-on-skipped' => null, + 'report-useless-tests' => null, + 'strict-coverage' => null, + 'disallow-test-output' => null, + 'enforce-time-limit' => null, + 'disallow-todo-tests' => null, + 'strict-global-state' => null, + 'strict' => null, + 'tap' => null, + 'testdox' => null, + 'testdox-html=' => null, + 'testdox-text=' => null, + 'test-suffix=' => null, + 'no-configuration' => null, + 'no-coverage' => null, + 'no-globals-backup' => null, + 'printer=' => null, + 'static-backup' => null, + 'verbose' => null, + 'version' => null + ); + + /** + * @var bool + */ + private $versionStringPrinted = false; + + /** + * @param bool $exit + */ + public static function main($exit = true) + { + $command = new static; + + return $command->run($_SERVER['argv'], $exit); + } + + /** + * @param array $argv + * @param bool $exit + * + * @return int + */ + public function run(array $argv, $exit = true) + { + $this->handleArguments($argv); + + $runner = $this->createRunner(); + + if (is_object($this->arguments['test']) && + $this->arguments['test'] instanceof PHPUnit_Framework_Test) { + $suite = $this->arguments['test']; + } else { + $suite = $runner->getTest( + $this->arguments['test'], + $this->arguments['testFile'], + $this->arguments['testSuffixes'] + ); + } + + if ($this->arguments['listGroups']) { + $this->printVersionString(); + + print "Available test group(s):\n"; + + $groups = $suite->getGroups(); + sort($groups); + + foreach ($groups as $group) { + print " - $group\n"; + } + + if ($exit) { + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + } else { + return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; + } + } + + unset($this->arguments['test']); + unset($this->arguments['testFile']); + + try { + $result = $runner->doRun($suite, $this->arguments); + } catch (PHPUnit_Framework_Exception $e) { + print $e->getMessage() . "\n"; + } + + $ret = PHPUnit_TextUI_TestRunner::FAILURE_EXIT; + + if (isset($result) && $result->wasSuccessful()) { + $ret = PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; + } elseif (!isset($result) || $result->errorCount() > 0) { + $ret = PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT; + } + + if ($exit) { + exit($ret); + } else { + return $ret; + } + } + + /** + * Create a TestRunner, override in subclasses. + * + * @return PHPUnit_TextUI_TestRunner + * + * @since Method available since Release 3.6.0 + */ + protected function createRunner() + { + return new PHPUnit_TextUI_TestRunner($this->arguments['loader']); + } + + /** + * Handles the command-line arguments. + * + * A child class of PHPUnit_TextUI_Command can hook into the argument + * parsing by adding the switch(es) to the $longOptions array and point to a + * callback method that handles the switch(es) in the child class like this + * + * + * longOptions['my-switch'] = 'myHandler'; + * // my-secondswitch will accept a value - note the equals sign + * $this->longOptions['my-secondswitch='] = 'myOtherHandler'; + * } + * + * // --my-switch -> myHandler() + * protected function myHandler() + * { + * } + * + * // --my-secondswitch foo -> myOtherHandler('foo') + * protected function myOtherHandler ($value) + * { + * } + * + * // You will also need this - the static keyword in the + * // PHPUnit_TextUI_Command will mean that it'll be + * // PHPUnit_TextUI_Command that gets instantiated, + * // not MyCommand + * public static function main($exit = true) + * { + * $command = new static; + * + * return $command->run($_SERVER['argv'], $exit); + * } + * + * } + * + * + * @param array $argv + */ + protected function handleArguments(array $argv) + { + if (defined('__PHPUNIT_PHAR__')) { + $this->longOptions['check-version'] = null; + $this->longOptions['selfupdate'] = null; + $this->longOptions['self-update'] = null; + $this->longOptions['selfupgrade'] = null; + $this->longOptions['self-upgrade'] = null; + } + + try { + $this->options = PHPUnit_Util_Getopt::getopt( + $argv, + 'd:c:hv', + array_keys($this->longOptions) + ); + } catch (PHPUnit_Framework_Exception $e) { + $this->showError($e->getMessage()); + } + + foreach ($this->options[0] as $option) { + switch ($option[0]) { + case '--colors': + $this->arguments['colors'] = $option[1] ?: PHPUnit_TextUI_ResultPrinter::COLOR_AUTO; + break; + + case '--bootstrap': + $this->arguments['bootstrap'] = $option[1]; + break; + + case '--columns': + if (is_numeric($option[1])) { + $this->arguments['columns'] = (int) $option[1]; + } elseif ($option[1] == 'max') { + $this->arguments['columns'] = 'max'; + } + break; + + case 'c': + case '--configuration': + $this->arguments['configuration'] = $option[1]; + break; + + case '--coverage-clover': + $this->arguments['coverageClover'] = $option[1]; + break; + + case '--coverage-crap4j': + $this->arguments['coverageCrap4J'] = $option[1]; + break; + + case '--coverage-html': + $this->arguments['coverageHtml'] = $option[1]; + break; + + case '--coverage-php': + $this->arguments['coveragePHP'] = $option[1]; + break; + + case '--coverage-text': + if ($option[1] === null) { + $option[1] = 'php://stdout'; + } + + $this->arguments['coverageText'] = $option[1]; + $this->arguments['coverageTextShowUncoveredFiles'] = false; + $this->arguments['coverageTextShowOnlySummary'] = false; + break; + + case '--coverage-xml': + $this->arguments['coverageXml'] = $option[1]; + break; + + case 'd': + $ini = explode('=', $option[1]); + + if (isset($ini[0])) { + if (isset($ini[1])) { + ini_set($ini[0], $ini[1]); + } else { + ini_set($ini[0], true); + } + } + break; + + case '--debug': + $this->arguments['debug'] = true; + break; + + case 'h': + case '--help': + $this->showHelp(); + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + break; + + case '--filter': + $this->arguments['filter'] = $option[1]; + break; + + case '--testsuite': + $this->arguments['testsuite'] = $option[1]; + break; + + case '--group': + $this->arguments['groups'] = explode(',', $option[1]); + break; + + case '--exclude-group': + $this->arguments['excludeGroups'] = explode( + ',', + $option[1] + ); + break; + + case '--test-suffix': + $this->arguments['testSuffixes'] = explode( + ',', + $option[1] + ); + break; + + case '--include-path': + $includePath = $option[1]; + break; + + case '--list-groups': + $this->arguments['listGroups'] = true; + break; + + case '--printer': + $this->arguments['printer'] = $option[1]; + break; + + case '--loader': + $this->arguments['loader'] = $option[1]; + break; + + case '--log-json': + $this->arguments['jsonLogfile'] = $option[1]; + break; + + case '--log-junit': + $this->arguments['junitLogfile'] = $option[1]; + break; + + case '--log-tap': + $this->arguments['tapLogfile'] = $option[1]; + break; + + case '--process-isolation': + $this->arguments['processIsolation'] = true; + break; + + case '--repeat': + $this->arguments['repeat'] = (int) $option[1]; + break; + + case '--stderr': + $this->arguments['stderr'] = true; + break; + + case '--stop-on-error': + $this->arguments['stopOnError'] = true; + break; + + case '--stop-on-failure': + $this->arguments['stopOnFailure'] = true; + break; + + case '--stop-on-incomplete': + $this->arguments['stopOnIncomplete'] = true; + break; + + case '--stop-on-risky': + $this->arguments['stopOnRisky'] = true; + break; + + case '--stop-on-skipped': + $this->arguments['stopOnSkipped'] = true; + break; + + case '--tap': + $this->arguments['printer'] = 'PHPUnit_Util_Log_TAP'; + break; + + case '--testdox': + $this->arguments['printer'] = 'PHPUnit_Util_TestDox_ResultPrinter_Text'; + break; + + case '--testdox-html': + $this->arguments['testdoxHTMLFile'] = $option[1]; + break; + + case '--testdox-text': + $this->arguments['testdoxTextFile'] = $option[1]; + break; + + case '--no-configuration': + $this->arguments['useDefaultConfiguration'] = false; + break; + + case '--no-coverage': + $this->arguments['noCoverage'] = true; + break; + + case '--no-globals-backup': + $this->arguments['backupGlobals'] = false; + break; + + case '--static-backup': + $this->arguments['backupStaticAttributes'] = true; + break; + + case 'v': + case '--verbose': + $this->arguments['verbose'] = true; + break; + + case '--version': + $this->printVersionString(); + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + break; + + case '--report-useless-tests': + $this->arguments['reportUselessTests'] = true; + break; + + case '--strict-coverage': + $this->arguments['strictCoverage'] = true; + break; + + case '--strict-global-state': + $this->arguments['disallowChangesToGlobalState'] = true; + break; + + case '--disallow-test-output': + $this->arguments['disallowTestOutput'] = true; + break; + + case '--enforce-time-limit': + $this->arguments['enforceTimeLimit'] = true; + break; + + case '--disallow-todo-tests': + $this->arguments['disallowTodoAnnotatedTests'] = true; + break; + + case '--strict': + $this->arguments['reportUselessTests'] = true; + $this->arguments['strictCoverage'] = true; + $this->arguments['disallowTestOutput'] = true; + $this->arguments['enforceTimeLimit'] = true; + $this->arguments['disallowTodoAnnotatedTests'] = true; + $this->arguments['deprecatedStrictModeOption'] = true; + break; + + case '--check-version': + $this->handleVersionCheck(); + break; + + case '--selfupdate': + case '--self-update': + $this->handleSelfUpdate(); + break; + + case '--selfupgrade': + case '--self-upgrade': + $this->handleSelfUpdate(true); + break; + + case '--whitelist': + $this->arguments['whitelist'] = $option[1]; + break; + + default: + $optionName = str_replace('--', '', $option[0]); + + if (isset($this->longOptions[$optionName])) { + $handler = $this->longOptions[$optionName]; + } elseif (isset($this->longOptions[$optionName . '='])) { + $handler = $this->longOptions[$optionName . '=']; + } + + if (isset($handler) && is_callable(array($this, $handler))) { + $this->$handler($option[1]); + } + } + } + + $this->handleCustomTestSuite(); + + if (!isset($this->arguments['test'])) { + if (isset($this->options[1][0])) { + $this->arguments['test'] = $this->options[1][0]; + } + + if (isset($this->options[1][1])) { + $this->arguments['testFile'] = realpath($this->options[1][1]); + } else { + $this->arguments['testFile'] = ''; + } + + if (isset($this->arguments['test']) && + is_file($this->arguments['test']) && + substr($this->arguments['test'], -5, 5) != '.phpt') { + $this->arguments['testFile'] = realpath($this->arguments['test']); + $this->arguments['test'] = substr($this->arguments['test'], 0, strrpos($this->arguments['test'], '.')); + } + } + + if (!isset($this->arguments['testSuffixes'])) { + $this->arguments['testSuffixes'] = array('Test.php', '.phpt'); + } + + if (isset($includePath)) { + ini_set( + 'include_path', + $includePath . PATH_SEPARATOR . ini_get('include_path') + ); + } + + if ($this->arguments['loader'] !== null) { + $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); + } + + if (isset($this->arguments['configuration']) && + is_dir($this->arguments['configuration'])) { + $configurationFile = $this->arguments['configuration'] . '/phpunit.xml'; + + if (file_exists($configurationFile)) { + $this->arguments['configuration'] = realpath( + $configurationFile + ); + } elseif (file_exists($configurationFile . '.dist')) { + $this->arguments['configuration'] = realpath( + $configurationFile . '.dist' + ); + } + } elseif (!isset($this->arguments['configuration']) && + $this->arguments['useDefaultConfiguration']) { + if (file_exists('phpunit.xml')) { + $this->arguments['configuration'] = realpath('phpunit.xml'); + } elseif (file_exists('phpunit.xml.dist')) { + $this->arguments['configuration'] = realpath( + 'phpunit.xml.dist' + ); + } + } + + if (isset($this->arguments['configuration'])) { + try { + $configuration = PHPUnit_Util_Configuration::getInstance( + $this->arguments['configuration'] + ); + } catch (Throwable $e) { + print $e->getMessage() . "\n"; + exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); + } catch (Exception $e) { + print $e->getMessage() . "\n"; + exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); + } + + $phpunit = $configuration->getPHPUnitConfiguration(); + + $configuration->handlePHPConfiguration(); + + /* + * Issue #1216 + */ + if (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } elseif (isset($phpunit['bootstrap'])) { + $this->handleBootstrap($phpunit['bootstrap']); + } + + /* + * Issue #657 + */ + if (isset($phpunit['stderr']) && ! isset($this->arguments['stderr'])) { + $this->arguments['stderr'] = $phpunit['stderr']; + } + + if (isset($phpunit['columns']) && ! isset($this->arguments['columns'])) { + $this->arguments['columns'] = $phpunit['columns']; + } + + if (isset($phpunit['printerClass'])) { + if (isset($phpunit['printerFile'])) { + $file = $phpunit['printerFile']; + } else { + $file = ''; + } + + $this->arguments['printer'] = $this->handlePrinter( + $phpunit['printerClass'], + $file + ); + } + + if (isset($phpunit['testSuiteLoaderClass'])) { + if (isset($phpunit['testSuiteLoaderFile'])) { + $file = $phpunit['testSuiteLoaderFile']; + } else { + $file = ''; + } + + $this->arguments['loader'] = $this->handleLoader( + $phpunit['testSuiteLoaderClass'], + $file + ); + } + + $browsers = $configuration->getSeleniumBrowserConfiguration(); + + if (!empty($browsers)) { + $this->arguments['deprecatedSeleniumConfiguration'] = true; + + if (class_exists('PHPUnit_Extensions_SeleniumTestCase')) { + PHPUnit_Extensions_SeleniumTestCase::$browsers = $browsers; + } + } + + if (!isset($this->arguments['test'])) { + $testSuite = $configuration->getTestSuiteConfiguration(isset($this->arguments['testsuite']) ? $this->arguments['testsuite'] : null); + + if ($testSuite !== null) { + $this->arguments['test'] = $testSuite; + } + } + } elseif (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } + + if (isset($this->arguments['printer']) && + is_string($this->arguments['printer'])) { + $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); + } + + if (isset($this->arguments['test']) && is_string($this->arguments['test']) && substr($this->arguments['test'], -5, 5) == '.phpt') { + $test = new PHPUnit_Extensions_PhptTestCase($this->arguments['test']); + + $this->arguments['test'] = new PHPUnit_Framework_TestSuite; + $this->arguments['test']->addTest($test); + } + + if (!isset($this->arguments['test']) || + (isset($this->arguments['testDatabaseLogRevision']) && !isset($this->arguments['testDatabaseDSN']))) { + $this->showHelp(); + exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); + } + } + + /** + * Handles the loading of the PHPUnit_Runner_TestSuiteLoader implementation. + * + * @param string $loaderClass + * @param string $loaderFile + * + * @return PHPUnit_Runner_TestSuiteLoader + */ + protected function handleLoader($loaderClass, $loaderFile = '') + { + if (!class_exists($loaderClass, false)) { + if ($loaderFile == '') { + $loaderFile = PHPUnit_Util_Filesystem::classNameToFilename( + $loaderClass + ); + } + + $loaderFile = stream_resolve_include_path($loaderFile); + + if ($loaderFile) { + require $loaderFile; + } + } + + if (class_exists($loaderClass, false)) { + $class = new ReflectionClass($loaderClass); + + if ($class->implementsInterface('PHPUnit_Runner_TestSuiteLoader') && + $class->isInstantiable()) { + return $class->newInstance(); + } + } + + if ($loaderClass == 'PHPUnit_Runner_StandardTestSuiteLoader') { + return; + } + + $this->showError( + sprintf( + 'Could not use "%s" as loader.', + $loaderClass + ) + ); + } + + /** + * Handles the loading of the PHPUnit_Util_Printer implementation. + * + * @param string $printerClass + * @param string $printerFile + * + * @return PHPUnit_Util_Printer + */ + protected function handlePrinter($printerClass, $printerFile = '') + { + if (!class_exists($printerClass, false)) { + if ($printerFile == '') { + $printerFile = PHPUnit_Util_Filesystem::classNameToFilename( + $printerClass + ); + } + + $printerFile = stream_resolve_include_path($printerFile); + + if ($printerFile) { + require $printerFile; + } + } + + if (class_exists($printerClass)) { + $class = new ReflectionClass($printerClass); + + if ($class->implementsInterface('PHPUnit_Framework_TestListener') && + $class->isSubclassOf('PHPUnit_Util_Printer') && + $class->isInstantiable()) { + if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) { + return $printerClass; + } + + $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; + + return $class->newInstance($outputStream); + } + } + + $this->showError( + sprintf( + 'Could not use "%s" as printer.', + $printerClass + ) + ); + } + + /** + * Loads a bootstrap file. + * + * @param string $filename + */ + protected function handleBootstrap($filename) + { + try { + PHPUnit_Util_Fileloader::checkAndLoad($filename); + } catch (PHPUnit_Framework_Exception $e) { + $this->showError($e->getMessage()); + } + } + + /** + * @since Method available since Release 4.0.0 + */ + protected function handleSelfUpdate($upgrade = false) + { + $this->printVersionString(); + + $localFilename = realpath($_SERVER['argv'][0]); + + if (!is_writable($localFilename)) { + print 'No write permission to update ' . $localFilename . "\n"; + exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); + } + + if (!extension_loaded('openssl')) { + print "The OpenSSL extension is not loaded.\n"; + exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); + } + + if (!$upgrade) { + $remoteFilename = sprintf( + 'https://phar.phpunit.de/phpunit-%s.phar', + file_get_contents( + sprintf( + 'https://phar.phpunit.de/latest-version-of/phpunit-%s', + PHPUnit_Runner_Version::series() + ) + ) + ); + } else { + $remoteFilename = sprintf( + 'https://phar.phpunit.de/phpunit%s.phar', + PHPUnit_Runner_Version::getReleaseChannel() + ); + } + + $tempFilename = tempnam(sys_get_temp_dir(), 'phpunit') . '.phar'; + + // Workaround for https://bugs.php.net/bug.php?id=65538 + $caFile = dirname($tempFilename) . '/ca.pem'; + copy(__PHPUNIT_PHAR_ROOT__ . '/ca.pem', $caFile); + + print 'Updating the PHPUnit PHAR ... '; + + $options = array( + 'ssl' => array( + 'allow_self_signed' => false, + 'cafile' => $caFile, + 'verify_peer' => true + ) + ); + + if (PHP_VERSION_ID < 50600) { + $options['ssl']['CN_match'] = 'phar.phpunit.de'; + $options['ssl']['SNI_server_name'] = 'phar.phpunit.de'; + } + + file_put_contents( + $tempFilename, + file_get_contents( + $remoteFilename, + false, + stream_context_create($options) + ) + ); + + chmod($tempFilename, 0777 & ~umask()); + + try { + $phar = new Phar($tempFilename); + unset($phar); + rename($tempFilename, $localFilename); + unlink($caFile); + } catch (Throwable $_e) { + $e = $_e; + } catch (Exception $_e) { + $e = $_e; + } + + if (isset($e)) { + unlink($caFile); + unlink($tempFilename); + print " done\n\n" . $e->getMessage() . "\n"; + exit(2); + } + + print " done\n"; + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + } + + /** + * @since Method available since Release 4.8.0 + */ + protected function handleVersionCheck() + { + $this->printVersionString(); + + $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); + $isOutdated = version_compare($latestVersion, PHPUnit_Runner_Version::id(), '>'); + + if ($isOutdated) { + print "You are not using the latest version of PHPUnit.\n"; + print 'Use "phpunit --self-upgrade" to install PHPUnit ' . $latestVersion . "\n"; + } else { + print "You are using the latest version of PHPUnit.\n"; + } + + exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); + } + + /** + * Show the help message. + */ + protected function showHelp() + { + $this->printVersionString(); + + print << + +Code Coverage Options: + + --coverage-clover Generate code coverage report in Clover XML format. + --coverage-crap4j Generate code coverage report in Crap4J XML format. + --coverage-html Generate code coverage report in HTML format. + --coverage-php Export PHP_CodeCoverage object to file. + --coverage-text= Generate code coverage report in text format. + Default: Standard output. + --coverage-xml Generate code coverage report in PHPUnit XML format. + +Logging Options: + + --log-junit Log test execution in JUnit XML format to file. + --log-tap Log test execution in TAP format to file. + --log-json Log test execution in JSON format. + --testdox-html Write agile documentation in HTML format to file. + --testdox-text Write agile documentation in Text format to file. + +Test Selection Options: + + --filter Filter which tests to run. + --testsuite Filter which testsuite to run. + --group ... Only runs tests from the specified group(s). + --exclude-group ... Exclude tests from the specified group(s). + --list-groups List available test groups. + --test-suffix ... Only search for test in files with specified + suffix(es). Default: Test.php,.phpt + +Test Execution Options: + + --report-useless-tests Be strict about tests that do not test anything. + --strict-coverage Be strict about unintentionally covered code. + --strict-global-state Be strict about changes to global state + --disallow-test-output Be strict about output during tests. + --enforce-time-limit Enforce time limit based on test size. + --disallow-todo-tests Disallow @todo-annotated tests. + + --process-isolation Run each test in a separate PHP process. + --no-globals-backup Do not backup and restore \$GLOBALS for each test. + --static-backup Backup and restore static attributes for each test. + + --colors= Use colors in output ("never", "auto" or "always"). + --columns Number of columns to use for progress output. + --columns max Use maximum number of columns for progress output. + --stderr Write to STDERR instead of STDOUT. + --stop-on-error Stop execution upon first error. + --stop-on-failure Stop execution upon first error or failure. + --stop-on-risky Stop execution upon first risky test. + --stop-on-skipped Stop execution upon first skipped test. + --stop-on-incomplete Stop execution upon first incomplete test. + -v|--verbose Output more verbose information. + --debug Display debugging information during test execution. + + --loader TestSuiteLoader implementation to use. + --repeat Runs the test(s) repeatedly. + --tap Report test execution progress in TAP format. + --testdox Report test execution progress in TestDox format. + --printer TestListener implementation to use. + +Configuration Options: + + --bootstrap A "bootstrap" PHP file that is run before the tests. + -c|--configuration Read configuration from XML file. + --no-configuration Ignore default configuration file (phpunit.xml). + --no-coverage Ignore code coverage configuration. + --include-path Prepend PHP's include_path with given path(s). + -d key[=value] Sets a php.ini value. + +Miscellaneous Options: + + -h|--help Prints this usage information. + --version Prints the version and exits. + +EOT; + + if (defined('__PHPUNIT_PHAR__')) { + print "\n --check-version Check whether PHPUnit is the latest version."; + print "\n --self-update Update PHPUnit to the latest version within the same\n release series.\n"; + print "\n --self-upgrade Upgrade PHPUnit to the latest version.\n"; + } + } + + /** + * Custom callback for test suite discovery. + */ + protected function handleCustomTestSuite() + { + } + + private function printVersionString() + { + if ($this->versionStringPrinted) { + return; + } + + print PHPUnit_Runner_Version::getVersionString() . "\n\n"; + + $this->versionStringPrinted = true; + } + + /** + */ + private function showError($message) + { + $this->printVersionString(); + + print $message . "\n"; + + exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SebastianBergmann\Environment\Console; + +/** + * Prints the result of a TextUI TestRunner run. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_TextUI_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + const EVENT_TEST_START = 0; + const EVENT_TEST_END = 1; + const EVENT_TESTSUITE_START = 2; + const EVENT_TESTSUITE_END = 3; + + const COLOR_NEVER = 'never'; + const COLOR_AUTO = 'auto'; + const COLOR_ALWAYS = 'always'; + const COLOR_DEFAULT = self::COLOR_NEVER; + + /** + * @var array + */ + private static $ansiCodes = array( + 'bold' => 1, + 'fg-black' => 30, + 'fg-red' => 31, + 'fg-green' => 32, + 'fg-yellow' => 33, + 'fg-blue' => 34, + 'fg-magenta' => 35, + 'fg-cyan' => 36, + 'fg-white' => 37, + 'bg-black' => 40, + 'bg-red' => 41, + 'bg-green' => 42, + 'bg-yellow' => 43, + 'bg-blue' => 44, + 'bg-magenta' => 45, + 'bg-cyan' => 46, + 'bg-white' => 47 + ); + + /** + * @var int + */ + protected $column = 0; + + /** + * @var int + */ + protected $maxColumn; + + /** + * @var bool + */ + protected $lastTestFailed = false; + + /** + * @var int + */ + protected $numAssertions = 0; + + /** + * @var int + */ + protected $numTests = -1; + + /** + * @var int + */ + protected $numTestsRun = 0; + + /** + * @var int + */ + protected $numTestsWidth; + + /** + * @var bool + */ + protected $colors = false; + + /** + * @var bool + */ + protected $debug = false; + + /** + * @var bool + */ + protected $verbose = false; + + /** + * @var int + */ + private $numberOfColumns; + + /** + * Constructor. + * + * @param mixed $out + * @param bool $verbose + * @param string $colors + * @param bool $debug + * @param int|string $numberOfColumns + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.0.0 + */ + public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80) + { + parent::__construct($out); + + if (!is_bool($verbose)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean'); + } + + $availableColors = array(self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS); + + if (!in_array($colors, $availableColors)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 3, + vsprintf('value from "%s", "%s" or "%s"', $availableColors) + ); + } + + if (!is_bool($debug)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean'); + } + + if (!is_int($numberOfColumns) && $numberOfColumns != 'max') { + throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'integer or "max"'); + } + + $console = new Console; + $maxNumberOfColumns = $console->getNumberOfColumns(); + + if ($numberOfColumns == 'max' || $numberOfColumns > $maxNumberOfColumns) { + $numberOfColumns = $maxNumberOfColumns; + } + + $this->numberOfColumns = $numberOfColumns; + $this->verbose = $verbose; + $this->debug = $debug; + + if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) { + $this->colors = true; + } else { + $this->colors = (self::COLOR_ALWAYS === $colors); + } + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + public function printResult(PHPUnit_Framework_TestResult $result) + { + $this->printHeader(); + + $this->printErrors($result); + $printSeparator = $result->errorCount() > 0; + + if ($printSeparator && $result->failureCount() > 0) { + $this->write("\n--\n\n"); + } + + $printSeparator = $printSeparator || $result->failureCount() > 0; + $this->printFailures($result); + + if ($this->verbose) { + if ($printSeparator && $result->riskyCount() > 0) { + $this->write("\n--\n\n"); + } + + $printSeparator = $printSeparator || + $result->riskyCount() > 0; + + $this->printRisky($result); + + if ($printSeparator && $result->notImplementedCount() > 0) { + $this->write("\n--\n\n"); + } + + $printSeparator = $printSeparator || + $result->notImplementedCount() > 0; + + $this->printIncompletes($result); + + if ($printSeparator && $result->skippedCount() > 0) { + $this->write("\n--\n\n"); + } + + $this->printSkipped($result); + } + + $this->printFooter($result); + } + + /** + * @param array $defects + * @param string $type + */ + protected function printDefects(array $defects, $type) + { + $count = count($defects); + + if ($count == 0) { + return; + } + + $this->write( + sprintf( + "There %s %d %s%s:\n", + ($count == 1) ? 'was' : 'were', + $count, + $type, + ($count == 1) ? '' : 's' + ) + ); + + $i = 1; + + foreach ($defects as $defect) { + $this->printDefect($defect, $i++); + } + } + + /** + * @param PHPUnit_Framework_TestFailure $defect + * @param int $count + */ + protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count) + { + $this->printDefectHeader($defect, $count); + $this->printDefectTrace($defect); + } + + /** + * @param PHPUnit_Framework_TestFailure $defect + * @param int $count + */ + protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count) + { + $this->write( + sprintf( + "\n%d) %s\n", + $count, + $defect->getTestName() + ) + ); + } + + /** + * @param PHPUnit_Framework_TestFailure $defect + */ + protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect) + { + $e = $defect->thrownException(); + $this->write((string) $e); + + while ($e = $e->getPrevious()) { + $this->write("\nCaused by\n" . $e); + } + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printErrors(PHPUnit_Framework_TestResult $result) + { + $this->printDefects($result->errors(), 'error'); + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printFailures(PHPUnit_Framework_TestResult $result) + { + $this->printDefects($result->failures(), 'failure'); + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printIncompletes(PHPUnit_Framework_TestResult $result) + { + $this->printDefects($result->notImplemented(), 'incomplete test'); + } + + /** + * @param PHPUnit_Framework_TestResult $result + * + * @since Method available since Release 4.0.0 + */ + protected function printRisky(PHPUnit_Framework_TestResult $result) + { + $this->printDefects($result->risky(), 'risky test'); + } + + /** + * @param PHPUnit_Framework_TestResult $result + * + * @since Method available since Release 3.0.0 + */ + protected function printSkipped(PHPUnit_Framework_TestResult $result) + { + $this->printDefects($result->skipped(), 'skipped test'); + } + + protected function printHeader() + { + $this->write("\n\n" . PHP_Timer::resourceUsage() . "\n\n"); + } + + /** + * @param PHPUnit_Framework_TestResult $result + */ + protected function printFooter(PHPUnit_Framework_TestResult $result) + { + if (count($result) === 0) { + $this->writeWithColor( + 'fg-black, bg-yellow', + 'No tests executed!' + ); + } elseif ($result->wasSuccessful() && + $result->allHarmless() && + $result->allCompletelyImplemented() && + $result->noneSkipped()) { + $this->writeWithColor( + 'fg-black, bg-green', + sprintf( + 'OK (%d test%s, %d assertion%s)', + count($result), + (count($result) == 1) ? '' : 's', + $this->numAssertions, + ($this->numAssertions == 1) ? '' : 's' + ) + ); + } else { + if ($result->wasSuccessful()) { + $color = 'fg-black, bg-yellow'; + + if ($this->verbose) { + $this->write("\n"); + } + + $this->writeWithColor( + $color, + 'OK, but incomplete, skipped, or risky tests!' + ); + } else { + $color = 'fg-white, bg-red'; + + $this->write("\n"); + $this->writeWithColor($color, 'FAILURES!'); + } + + $this->writeCountString(count($result), 'Tests', $color, true); + $this->writeCountString($this->numAssertions, 'Assertions', $color, true); + $this->writeCountString($result->errorCount(), 'Errors', $color); + $this->writeCountString($result->failureCount(), 'Failures', $color); + $this->writeCountString($result->skippedCount(), 'Skipped', $color); + $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color); + $this->writeCountString($result->riskyCount(), 'Risky', $color); + $this->writeWithColor($color, '.', true); + } + } + + /** + */ + public function printWaitPrompt() + { + $this->write("\n to continue\n"); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeProgressWithColor('fg-red, bold', 'E'); + $this->lastTestFailed = true; + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->writeProgressWithColor('bg-red, fg-white', 'F'); + $this->lastTestFailed = true; + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeProgressWithColor('fg-yellow, bold', 'I'); + $this->lastTestFailed = true; + } + + /** + * Risky test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 4.0.0 + */ + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeProgressWithColor('fg-yellow, bold', 'R'); + $this->lastTestFailed = true; + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeProgressWithColor('fg-cyan, bold', 'S'); + $this->lastTestFailed = true; + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + if ($this->numTests == -1) { + $this->numTests = count($suite); + $this->numTestsWidth = strlen((string) $this->numTests); + $this->maxColumn = $this->numberOfColumns - strlen(' / (XXX%)') - (2 * $this->numTestsWidth); + } + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + if ($this->debug) { + $this->write( + sprintf( + "\nStarting test '%s'.\n", + PHPUnit_Util_Test::describe($test) + ) + ); + } + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if (!$this->lastTestFailed) { + $this->writeProgress('.'); + } + + if ($test instanceof PHPUnit_Framework_TestCase) { + $this->numAssertions += $test->getNumAssertions(); + } elseif ($test instanceof PHPUnit_Extensions_PhptTestCase) { + $this->numAssertions++; + } + + $this->lastTestFailed = false; + + if ($test instanceof PHPUnit_Framework_TestCase) { + if (!$test->hasExpectationOnOutput()) { + $this->write($test->getActualOutput()); + } + } + } + + /** + * @param string $progress + */ + protected function writeProgress($progress) + { + $this->write($progress); + $this->column++; + $this->numTestsRun++; + + if ($this->column == $this->maxColumn) { + $this->write( + sprintf( + ' %' . $this->numTestsWidth . 'd / %' . + $this->numTestsWidth . 'd (%3s%%)', + $this->numTestsRun, + $this->numTests, + floor(($this->numTestsRun / $this->numTests) * 100) + ) + ); + + $this->writeNewLine(); + } + } + + protected function writeNewLine() + { + $this->column = 0; + $this->write("\n"); + } + + /** + * Formats a buffer with a specified ANSI color sequence if colors are + * enabled. + * + * @param string $color + * @param string $buffer + * + * @return string + * + * @since Method available since Release 4.0.0 + */ + protected function formatWithColor($color, $buffer) + { + if (!$this->colors) { + return $buffer; + } + + $codes = array_map('trim', explode(',', $color)); + $lines = explode("\n", $buffer); + $padding = max(array_map('strlen', $lines)); + $styles = array(); + + foreach ($codes as $code) { + $styles[] = self::$ansiCodes[$code]; + } + + $style = sprintf("\x1b[%sm", implode(';', $styles)); + + $styledLines = array(); + + foreach ($lines as $line) { + $styledLines[] = $style . str_pad($line, $padding) . "\x1b[0m"; + } + + return implode("\n", $styledLines); + } + + /** + * Writes a buffer out with a color sequence if colors are enabled. + * + * @param string $color + * @param string $buffer + * @param bool $lf + * + * @since Method available since Release 4.0.0 + */ + protected function writeWithColor($color, $buffer, $lf = true) + { + $this->write($this->formatWithColor($color, $buffer)); + + if ($lf) { + $this->write("\n"); + } + } + + /** + * Writes progress with a color sequence if colors are enabled. + * + * @param string $color + * @param string $buffer + * + * @since Method available since Release 4.0.0 + */ + protected function writeProgressWithColor($color, $buffer) + { + $buffer = $this->formatWithColor($color, $buffer); + $this->writeProgress($buffer); + } + + /** + * @param int $count + * @param string $name + * @param string $color + * @param bool $always + * + * @since Method available since Release 4.6.5 + */ + private function writeCountString($count, $name, $color, $always = false) + { + static $first = true; + + if ($always || $count > 0) { + $this->writeWithColor( + $color, + sprintf( + '%s%s: %d', + !$first ? ', ' : '', + $name, + $count + ), + false + ); + + $first = false; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SebastianBergmann\Environment\Runtime; + +/** + * A TestRunner for the Command Line Interface (CLI) + * PHP SAPI Module. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_TextUI_TestRunner extends PHPUnit_Runner_BaseTestRunner +{ + const SUCCESS_EXIT = 0; + const FAILURE_EXIT = 1; + const EXCEPTION_EXIT = 2; + + /** + * @var PHP_CodeCoverage_Filter + */ + protected $codeCoverageFilter; + + /** + * @var PHPUnit_Runner_TestSuiteLoader + */ + protected $loader = null; + + /** + * @var PHPUnit_TextUI_ResultPrinter + */ + protected $printer = null; + + /** + * @var bool + */ + protected static $versionStringPrinted = false; + + /** + * @var array + */ + private $missingExtensions = array(); + + /** + * @var Runtime + */ + private $runtime; + + /** + * @param PHPUnit_Runner_TestSuiteLoader $loader + * @param PHP_CodeCoverage_Filter $filter + * + * @since Method available since Release 3.4.0 + */ + public function __construct(PHPUnit_Runner_TestSuiteLoader $loader = null, PHP_CodeCoverage_Filter $filter = null) + { + if ($filter === null) { + $filter = $this->getCodeCoverageFilter(); + } + + $this->codeCoverageFilter = $filter; + $this->loader = $loader; + $this->runtime = new Runtime; + } + + /** + * @param PHPUnit_Framework_Test|ReflectionClass $test + * @param array $arguments + * + * @return PHPUnit_Framework_TestResult + * + * @throws PHPUnit_Framework_Exception + */ + public static function run($test, array $arguments = array()) + { + if ($test instanceof ReflectionClass) { + $test = new PHPUnit_Framework_TestSuite($test); + } + + if ($test instanceof PHPUnit_Framework_Test) { + $aTestRunner = new self; + + return $aTestRunner->doRun( + $test, + $arguments + ); + } else { + throw new PHPUnit_Framework_Exception( + 'No test case or test suite found.' + ); + } + } + + /** + * @return PHPUnit_Framework_TestResult + */ + protected function createTestResult() + { + return new PHPUnit_Framework_TestResult; + } + + private function processSuiteFilters(PHPUnit_Framework_TestSuite $suite, array $arguments) + { + if (!$arguments['filter'] && + empty($arguments['groups']) && + empty($arguments['excludeGroups'])) { + return; + } + + $filterFactory = new PHPUnit_Runner_Filter_Factory(); + + if (!empty($arguments['excludeGroups'])) { + $filterFactory->addFilter( + new ReflectionClass('PHPUnit_Runner_Filter_Group_Exclude'), + $arguments['excludeGroups'] + ); + } + + if (!empty($arguments['groups'])) { + $filterFactory->addFilter( + new ReflectionClass('PHPUnit_Runner_Filter_Group_Include'), + $arguments['groups'] + ); + } + + if ($arguments['filter']) { + $filterFactory->addFilter( + new ReflectionClass('PHPUnit_Runner_Filter_Test'), + $arguments['filter'] + ); + } + $suite->injectFilter($filterFactory); + } + + /** + * @param PHPUnit_Framework_Test $suite + * @param array $arguments + * + * @return PHPUnit_Framework_TestResult + */ + public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) + { + if (isset($arguments['configuration'])) { + $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; + } + + $this->handleConfiguration($arguments); + + $this->processSuiteFilters($suite, $arguments); + + if (isset($arguments['bootstrap'])) { + $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; + } + + if ($arguments['backupGlobals'] === false) { + $suite->setBackupGlobals(false); + } + + if ($arguments['backupStaticAttributes'] === true) { + $suite->setBackupStaticAttributes(true); + } + + if ($arguments['disallowChangesToGlobalState'] === true) { + $suite->setDisallowChangesToGlobalState(true); + } + + if (is_integer($arguments['repeat'])) { + $test = new PHPUnit_Extensions_RepeatedTest( + $suite, + $arguments['repeat'], + $arguments['processIsolation'] + ); + + $suite = new PHPUnit_Framework_TestSuite(); + $suite->addTest($test); + } + + $result = $this->createTestResult(); + + if (!$arguments['convertErrorsToExceptions']) { + $result->convertErrorsToExceptions(false); + } + + if (!$arguments['convertNoticesToExceptions']) { + PHPUnit_Framework_Error_Notice::$enabled = false; + } + + if (!$arguments['convertWarningsToExceptions']) { + PHPUnit_Framework_Error_Warning::$enabled = false; + } + + if ($arguments['stopOnError']) { + $result->stopOnError(true); + } + + if ($arguments['stopOnFailure']) { + $result->stopOnFailure(true); + } + + if ($arguments['stopOnIncomplete']) { + $result->stopOnIncomplete(true); + } + + if ($arguments['stopOnRisky']) { + $result->stopOnRisky(true); + } + + if ($arguments['stopOnSkipped']) { + $result->stopOnSkipped(true); + } + + if ($this->printer === null) { + if (isset($arguments['printer']) && + $arguments['printer'] instanceof PHPUnit_Util_Printer) { + $this->printer = $arguments['printer']; + } else { + $printerClass = 'PHPUnit_TextUI_ResultPrinter'; + + if (isset($arguments['printer']) && + is_string($arguments['printer']) && + class_exists($arguments['printer'], false)) { + $class = new ReflectionClass($arguments['printer']); + + if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) { + $printerClass = $arguments['printer']; + } + } + + $this->printer = new $printerClass( + isset($arguments['stderr']) ? 'php://stderr' : null, + $arguments['verbose'], + $arguments['colors'], + $arguments['debug'], + $arguments['columns'] + ); + } + } + + if (!$this->printer instanceof PHPUnit_Util_Log_TAP) { + $this->printer->write( + PHPUnit_Runner_Version::getVersionString() . "\n" + ); + + self::$versionStringPrinted = true; + + if ($arguments['verbose']) { + $this->printer->write( + sprintf( + "\nRuntime:\t%s", + $this->runtime->getNameWithVersion() + ) + ); + + if ($this->runtime->hasXdebug()) { + $this->printer->write( + sprintf( + ' with Xdebug %s', + phpversion('xdebug') + ) + ); + } + + if (isset($arguments['configuration'])) { + $this->printer->write( + sprintf( + "\nConfiguration:\t%s", + $arguments['configuration']->getFilename() + ) + ); + } + + $this->printer->write("\n"); + } + + if (isset($arguments['deprecatedStrictModeOption'])) { + print "Warning:\tDeprecated option \"--strict\" used\n"; + } elseif (isset($arguments['deprecatedStrictModeSetting'])) { + print "Warning:\tDeprecated configuration setting \"strict\" used\n"; + } + + if (isset($arguments['deprecatedSeleniumConfiguration'])) { + print "Warning:\tDeprecated configuration setting \"selenium\" used\n"; + } + } + + foreach ($arguments['listeners'] as $listener) { + $result->addListener($listener); + } + + $result->addListener($this->printer); + + if (isset($arguments['testdoxHTMLFile'])) { + $result->addListener( + new PHPUnit_Util_TestDox_ResultPrinter_HTML( + $arguments['testdoxHTMLFile'] + ) + ); + } + + if (isset($arguments['testdoxTextFile'])) { + $result->addListener( + new PHPUnit_Util_TestDox_ResultPrinter_Text( + $arguments['testdoxTextFile'] + ) + ); + } + + $codeCoverageReports = 0; + + if (isset($arguments['coverageClover'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageCrap4J'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageHtml'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coveragePHP'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageText'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageXml'])) { + $codeCoverageReports++; + } + + if (isset($arguments['noCoverage'])) { + $codeCoverageReports = 0; + } + + if ($codeCoverageReports > 0 && (!extension_loaded('tokenizer') || !$this->runtime->canCollectCodeCoverage())) { + if (!extension_loaded('tokenizer')) { + $this->showExtensionNotLoadedWarning( + 'tokenizer', + 'No code coverage will be generated.' + ); + } elseif (!extension_loaded('Xdebug')) { + $this->showExtensionNotLoadedWarning( + 'Xdebug', + 'No code coverage will be generated.' + ); + } + + $codeCoverageReports = 0; + } + + if (!$this->printer instanceof PHPUnit_Util_Log_TAP) { + if ($codeCoverageReports > 0 && !$this->codeCoverageFilter->hasWhitelist()) { + $this->printer->write("Warning:\tNo whitelist configured for code coverage\n"); + } + + $this->printer->write("\n"); + } + + if ($codeCoverageReports > 0) { + $codeCoverage = new PHP_CodeCoverage( + null, + $this->codeCoverageFilter + ); + + $codeCoverage->setAddUncoveredFilesFromWhitelist( + $arguments['addUncoveredFilesFromWhitelist'] + ); + + $codeCoverage->setCheckForUnintentionallyCoveredCode( + $arguments['strictCoverage'] + ); + + $codeCoverage->setProcessUncoveredFilesFromWhitelist( + $arguments['processUncoveredFilesFromWhitelist'] + ); + + if (isset($arguments['forceCoversAnnotation'])) { + $codeCoverage->setForceCoversAnnotation( + $arguments['forceCoversAnnotation'] + ); + } + + if (isset($arguments['mapTestClassNameToCoveredClassName'])) { + $codeCoverage->setMapTestClassNameToCoveredClassName( + $arguments['mapTestClassNameToCoveredClassName'] + ); + } + + $result->setCodeCoverage($codeCoverage); + } + + if ($codeCoverageReports > 1) { + if (isset($arguments['cacheTokens'])) { + $codeCoverage->setCacheTokens($arguments['cacheTokens']); + } + } + + if (isset($arguments['jsonLogfile'])) { + $result->addListener( + new PHPUnit_Util_Log_JSON($arguments['jsonLogfile']) + ); + } + + if (isset($arguments['tapLogfile'])) { + $result->addListener( + new PHPUnit_Util_Log_TAP($arguments['tapLogfile']) + ); + } + + if (isset($arguments['junitLogfile'])) { + $result->addListener( + new PHPUnit_Util_Log_JUnit( + $arguments['junitLogfile'], + $arguments['logIncompleteSkipped'] + ) + ); + } + + $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); + $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); + $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); + $result->beStrictAboutTestSize($arguments['enforceTimeLimit']); + $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']); + $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']); + $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']); + + if ($suite instanceof PHPUnit_Framework_TestSuite) { + $suite->setRunTestInSeparateProcess($arguments['processIsolation']); + } + + $suite->run($result); + + unset($suite); + $result->flushListeners(); + + if ($this->printer instanceof PHPUnit_TextUI_ResultPrinter) { + $this->printer->printResult($result); + } + + if (isset($codeCoverage)) { + if (isset($arguments['coverageClover'])) { + $this->printer->write( + "\nGenerating code coverage report in Clover XML format ..." + ); + + try { + $writer = new PHP_CodeCoverage_Report_Clover; + $writer->process($codeCoverage, $arguments['coverageClover']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (PHP_CodeCoverage_Exception $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coverageCrap4J'])) { + $this->printer->write( + "\nGenerating Crap4J report XML file ..." + ); + + try { + $writer = new PHP_CodeCoverage_Report_Crap4j($arguments['crap4jThreshold']); + $writer->process($codeCoverage, $arguments['coverageCrap4J']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (PHP_CodeCoverage_Exception $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coverageHtml'])) { + $this->printer->write( + "\nGenerating code coverage report in HTML format ..." + ); + + try { + $writer = new PHP_CodeCoverage_Report_HTML( + $arguments['reportLowUpperBound'], + $arguments['reportHighLowerBound'], + sprintf( + ' and PHPUnit %s', + PHPUnit_Runner_Version::id() + ) + ); + + $writer->process($codeCoverage, $arguments['coverageHtml']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (PHP_CodeCoverage_Exception $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coveragePHP'])) { + $this->printer->write( + "\nGenerating code coverage report in PHP format ..." + ); + + try { + $writer = new PHP_CodeCoverage_Report_PHP; + $writer->process($codeCoverage, $arguments['coveragePHP']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (PHP_CodeCoverage_Exception $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coverageText'])) { + if ($arguments['coverageText'] == 'php://stdout') { + $outputStream = $this->printer; + $colors = $arguments['colors'] && $arguments['colors'] != PHPUnit_TextUI_ResultPrinter::COLOR_NEVER; + } else { + $outputStream = new PHPUnit_Util_Printer($arguments['coverageText']); + $colors = false; + } + + $processor = new PHP_CodeCoverage_Report_Text( + $arguments['reportLowUpperBound'], + $arguments['reportHighLowerBound'], + $arguments['coverageTextShowUncoveredFiles'], + $arguments['coverageTextShowOnlySummary'] + ); + + $outputStream->write( + $processor->process($codeCoverage, $colors) + ); + } + + if (isset($arguments['coverageXml'])) { + $this->printer->write( + "\nGenerating code coverage report in PHPUnit XML format ..." + ); + + try { + $writer = new PHP_CodeCoverage_Report_XML; + $writer->process($codeCoverage, $arguments['coverageXml']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (PHP_CodeCoverage_Exception $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + } + + return $result; + } + + /** + * @param PHPUnit_TextUI_ResultPrinter $resultPrinter + */ + public function setPrinter(PHPUnit_TextUI_ResultPrinter $resultPrinter) + { + $this->printer = $resultPrinter; + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + * + * @param string $message + */ + protected function runFailed($message) + { + $this->write($message . PHP_EOL); + exit(self::FAILURE_EXIT); + } + + /** + * @param string $buffer + * + * @since Method available since Release 3.1.0 + */ + protected function write($buffer) + { + if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') { + $buffer = htmlspecialchars($buffer); + } + + if ($this->printer !== null) { + $this->printer->write($buffer); + } else { + print $buffer; + } + } + + /** + * Returns the loader to be used. + * + * @return PHPUnit_Runner_TestSuiteLoader + * + * @since Method available since Release 2.2.0 + */ + public function getLoader() + { + if ($this->loader === null) { + $this->loader = new PHPUnit_Runner_StandardTestSuiteLoader; + } + + return $this->loader; + } + + /** + * @param array $arguments + * + * @since Method available since Release 3.2.1 + */ + protected function handleConfiguration(array &$arguments) + { + if (isset($arguments['configuration']) && + !$arguments['configuration'] instanceof PHPUnit_Util_Configuration) { + $arguments['configuration'] = PHPUnit_Util_Configuration::getInstance( + $arguments['configuration'] + ); + } + + $arguments['debug'] = isset($arguments['debug']) ? $arguments['debug'] : false; + $arguments['filter'] = isset($arguments['filter']) ? $arguments['filter'] : false; + $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); + + if (isset($arguments['configuration'])) { + $arguments['configuration']->handlePHPConfiguration(); + + $phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration(); + + if (isset($phpunitConfiguration['deprecatedStrictModeSetting'])) { + $arguments['deprecatedStrictModeSetting'] = true; + } + + if (isset($phpunitConfiguration['backupGlobals']) && + !isset($arguments['backupGlobals'])) { + $arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals']; + } + + if (isset($phpunitConfiguration['backupStaticAttributes']) && + !isset($arguments['backupStaticAttributes'])) { + $arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes']; + } + + if (isset($phpunitConfiguration['disallowChangesToGlobalState']) && + !isset($arguments['disallowChangesToGlobalState'])) { + $arguments['disallowChangesToGlobalState'] = $phpunitConfiguration['disallowChangesToGlobalState']; + } + + if (isset($phpunitConfiguration['bootstrap']) && + !isset($arguments['bootstrap'])) { + $arguments['bootstrap'] = $phpunitConfiguration['bootstrap']; + } + + if (isset($phpunitConfiguration['cacheTokens']) && + !isset($arguments['cacheTokens'])) { + $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens']; + } + + if (isset($phpunitConfiguration['colors']) && + !isset($arguments['colors'])) { + $arguments['colors'] = $phpunitConfiguration['colors']; + } + + if (isset($phpunitConfiguration['convertErrorsToExceptions']) && + !isset($arguments['convertErrorsToExceptions'])) { + $arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions']; + } + + if (isset($phpunitConfiguration['convertNoticesToExceptions']) && + !isset($arguments['convertNoticesToExceptions'])) { + $arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions']; + } + + if (isset($phpunitConfiguration['convertWarningsToExceptions']) && + !isset($arguments['convertWarningsToExceptions'])) { + $arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions']; + } + + if (isset($phpunitConfiguration['processIsolation']) && + !isset($arguments['processIsolation'])) { + $arguments['processIsolation'] = $phpunitConfiguration['processIsolation']; + } + + if (isset($phpunitConfiguration['stopOnError']) && + !isset($arguments['stopOnError'])) { + $arguments['stopOnError'] = $phpunitConfiguration['stopOnError']; + } + + if (isset($phpunitConfiguration['stopOnFailure']) && + !isset($arguments['stopOnFailure'])) { + $arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure']; + } + + if (isset($phpunitConfiguration['stopOnIncomplete']) && + !isset($arguments['stopOnIncomplete'])) { + $arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete']; + } + + if (isset($phpunitConfiguration['stopOnRisky']) && + !isset($arguments['stopOnRisky'])) { + $arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky']; + } + + if (isset($phpunitConfiguration['stopOnSkipped']) && + !isset($arguments['stopOnSkipped'])) { + $arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped']; + } + + if (isset($phpunitConfiguration['timeoutForSmallTests']) && + !isset($arguments['timeoutForSmallTests'])) { + $arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests']; + } + + if (isset($phpunitConfiguration['timeoutForMediumTests']) && + !isset($arguments['timeoutForMediumTests'])) { + $arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests']; + } + + if (isset($phpunitConfiguration['timeoutForLargeTests']) && + !isset($arguments['timeoutForLargeTests'])) { + $arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests']; + } + + if (isset($phpunitConfiguration['reportUselessTests']) && + !isset($arguments['reportUselessTests'])) { + $arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests']; + } + + if (isset($phpunitConfiguration['strictCoverage']) && + !isset($arguments['strictCoverage'])) { + $arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage']; + } + + if (isset($phpunitConfiguration['disallowTestOutput']) && + !isset($arguments['disallowTestOutput'])) { + $arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput']; + } + + if (isset($phpunitConfiguration['enforceTimeLimit']) && + !isset($arguments['enforceTimeLimit'])) { + $arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit']; + } + + if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) && + !isset($arguments['disallowTodoAnnotatedTests'])) { + $arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests']; + } + + if (isset($phpunitConfiguration['verbose']) && + !isset($arguments['verbose'])) { + $arguments['verbose'] = $phpunitConfiguration['verbose']; + } + + if (isset($phpunitConfiguration['forceCoversAnnotation']) && + !isset($arguments['forceCoversAnnotation'])) { + $arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation']; + } + + if (isset($phpunitConfiguration['mapTestClassNameToCoveredClassName']) && + !isset($arguments['mapTestClassNameToCoveredClassName'])) { + $arguments['mapTestClassNameToCoveredClassName'] = $phpunitConfiguration['mapTestClassNameToCoveredClassName']; + } + + $groupCliArgs = array(); + + if (!empty($arguments['groups'])) { + $groupCliArgs = $arguments['groups']; + } + + $groupConfiguration = $arguments['configuration']->getGroupConfiguration(); + + if (!empty($groupConfiguration['include']) && + !isset($arguments['groups'])) { + $arguments['groups'] = $groupConfiguration['include']; + } + + if (!empty($groupConfiguration['exclude']) && + !isset($arguments['excludeGroups'])) { + $arguments['excludeGroups'] = array_diff($groupConfiguration['exclude'], $groupCliArgs); + } + + foreach ($arguments['configuration']->getListenerConfiguration() as $listener) { + if (!class_exists($listener['class'], false) && + $listener['file'] !== '') { + require_once $listener['file']; + } + + if (class_exists($listener['class'])) { + if (count($listener['arguments']) == 0) { + $listener = new $listener['class']; + } else { + $listenerClass = new ReflectionClass( + $listener['class'] + ); + $listener = $listenerClass->newInstanceArgs( + $listener['arguments'] + ); + } + + if ($listener instanceof PHPUnit_Framework_TestListener) { + $arguments['listeners'][] = $listener; + } + } + } + + $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration(); + + if (isset($loggingConfiguration['coverage-clover']) && + !isset($arguments['coverageClover'])) { + $arguments['coverageClover'] = $loggingConfiguration['coverage-clover']; + } + + if (isset($loggingConfiguration['coverage-crap4j']) && + !isset($arguments['coverageCrap4J'])) { + $arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j']; + + if (isset($loggingConfiguration['crap4jThreshold']) && + !isset($arguments['crap4jThreshold'])) { + $arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold']; + } + } + + if (isset($loggingConfiguration['coverage-html']) && + !isset($arguments['coverageHtml'])) { + if (isset($loggingConfiguration['lowUpperBound']) && + !isset($arguments['reportLowUpperBound'])) { + $arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound']; + } + + if (isset($loggingConfiguration['highLowerBound']) && + !isset($arguments['reportHighLowerBound'])) { + $arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound']; + } + + $arguments['coverageHtml'] = $loggingConfiguration['coverage-html']; + } + + if (isset($loggingConfiguration['coverage-php']) && + !isset($arguments['coveragePHP'])) { + $arguments['coveragePHP'] = $loggingConfiguration['coverage-php']; + } + + if (isset($loggingConfiguration['coverage-text']) && + !isset($arguments['coverageText'])) { + $arguments['coverageText'] = $loggingConfiguration['coverage-text']; + if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) { + $arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles']; + } else { + $arguments['coverageTextShowUncoveredFiles'] = false; + } + if (isset($loggingConfiguration['coverageTextShowOnlySummary'])) { + $arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary']; + } else { + $arguments['coverageTextShowOnlySummary'] = false; + } + } + + if (isset($loggingConfiguration['coverage-xml']) && + !isset($arguments['coverageXml'])) { + $arguments['coverageXml'] = $loggingConfiguration['coverage-xml']; + } + + if (isset($loggingConfiguration['json']) && + !isset($arguments['jsonLogfile'])) { + $arguments['jsonLogfile'] = $loggingConfiguration['json']; + } + + if (isset($loggingConfiguration['plain'])) { + $arguments['listeners'][] = new PHPUnit_TextUI_ResultPrinter( + $loggingConfiguration['plain'], + true + ); + } + + if (isset($loggingConfiguration['tap']) && + !isset($arguments['tapLogfile'])) { + $arguments['tapLogfile'] = $loggingConfiguration['tap']; + } + + if (isset($loggingConfiguration['junit']) && + !isset($arguments['junitLogfile'])) { + $arguments['junitLogfile'] = $loggingConfiguration['junit']; + + if (isset($loggingConfiguration['logIncompleteSkipped']) && + !isset($arguments['logIncompleteSkipped'])) { + $arguments['logIncompleteSkipped'] = $loggingConfiguration['logIncompleteSkipped']; + } + } + + if (isset($loggingConfiguration['testdox-html']) && + !isset($arguments['testdoxHTMLFile'])) { + $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html']; + } + + if (isset($loggingConfiguration['testdox-text']) && + !isset($arguments['testdoxTextFile'])) { + $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text']; + } + + if ((isset($arguments['coverageClover']) || + isset($arguments['coverageCrap4J']) || + isset($arguments['coverageHtml']) || + isset($arguments['coveragePHP']) || + isset($arguments['coverageText']) || + isset($arguments['coverageXml'])) && + $this->runtime->canCollectCodeCoverage()) { + $filterConfiguration = $arguments['configuration']->getFilterConfiguration(); + $arguments['addUncoveredFilesFromWhitelist'] = $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist']; + $arguments['processUncoveredFilesFromWhitelist'] = $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist']; + + if (empty($filterConfiguration['whitelist']['include']['directory']) && + empty($filterConfiguration['whitelist']['include']['file'])) { + foreach ($filterConfiguration['blacklist']['include']['directory'] as $dir) { + $this->codeCoverageFilter->addDirectoryToBlacklist( + $dir['path'], + $dir['suffix'], + $dir['prefix'], + $dir['group'] + ); + } + + foreach ($filterConfiguration['blacklist']['include']['file'] as $file) { + $this->codeCoverageFilter->addFileToBlacklist($file); + } + + foreach ($filterConfiguration['blacklist']['exclude']['directory'] as $dir) { + $this->codeCoverageFilter->removeDirectoryFromBlacklist( + $dir['path'], + $dir['suffix'], + $dir['prefix'], + $dir['group'] + ); + } + + foreach ($filterConfiguration['blacklist']['exclude']['file'] as $file) { + $this->codeCoverageFilter->removeFileFromBlacklist($file); + } + } + + foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) { + $this->codeCoverageFilter->addDirectoryToWhitelist( + $dir['path'], + $dir['suffix'], + $dir['prefix'] + ); + } + + foreach ($filterConfiguration['whitelist']['include']['file'] as $file) { + $this->codeCoverageFilter->addFileToWhitelist($file); + } + + foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) { + $this->codeCoverageFilter->removeDirectoryFromWhitelist( + $dir['path'], + $dir['suffix'], + $dir['prefix'] + ); + } + + foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) { + $this->codeCoverageFilter->removeFileFromWhitelist($file); + } + } + } + + $arguments['addUncoveredFilesFromWhitelist'] = isset($arguments['addUncoveredFilesFromWhitelist']) ? $arguments['addUncoveredFilesFromWhitelist'] : true; + $arguments['processUncoveredFilesFromWhitelist'] = isset($arguments['processUncoveredFilesFromWhitelist']) ? $arguments['processUncoveredFilesFromWhitelist'] : false; + $arguments['backupGlobals'] = isset($arguments['backupGlobals']) ? $arguments['backupGlobals'] : null; + $arguments['backupStaticAttributes'] = isset($arguments['backupStaticAttributes']) ? $arguments['backupStaticAttributes'] : null; + $arguments['disallowChangesToGlobalState'] = isset($arguments['disallowChangesToGlobalState']) ? $arguments['disallowChangesToGlobalState'] : null; + $arguments['cacheTokens'] = isset($arguments['cacheTokens']) ? $arguments['cacheTokens'] : false; + $arguments['columns'] = isset($arguments['columns']) ? $arguments['columns'] : 80; + $arguments['colors'] = isset($arguments['colors']) ? $arguments['colors'] : PHPUnit_TextUI_ResultPrinter::COLOR_DEFAULT; + $arguments['convertErrorsToExceptions'] = isset($arguments['convertErrorsToExceptions']) ? $arguments['convertErrorsToExceptions'] : true; + $arguments['convertNoticesToExceptions'] = isset($arguments['convertNoticesToExceptions']) ? $arguments['convertNoticesToExceptions'] : true; + $arguments['convertWarningsToExceptions'] = isset($arguments['convertWarningsToExceptions']) ? $arguments['convertWarningsToExceptions'] : true; + $arguments['excludeGroups'] = isset($arguments['excludeGroups']) ? $arguments['excludeGroups'] : array(); + $arguments['groups'] = isset($arguments['groups']) ? $arguments['groups'] : array(); + $arguments['logIncompleteSkipped'] = isset($arguments['logIncompleteSkipped']) ? $arguments['logIncompleteSkipped'] : false; + $arguments['processIsolation'] = isset($arguments['processIsolation']) ? $arguments['processIsolation'] : false; + $arguments['repeat'] = isset($arguments['repeat']) ? $arguments['repeat'] : false; + $arguments['reportHighLowerBound'] = isset($arguments['reportHighLowerBound']) ? $arguments['reportHighLowerBound'] : 90; + $arguments['reportLowUpperBound'] = isset($arguments['reportLowUpperBound']) ? $arguments['reportLowUpperBound'] : 50; + $arguments['crap4jThreshold'] = isset($arguments['crap4jThreshold']) ? $arguments['crap4jThreshold'] : 30; + $arguments['stopOnError'] = isset($arguments['stopOnError']) ? $arguments['stopOnError'] : false; + $arguments['stopOnFailure'] = isset($arguments['stopOnFailure']) ? $arguments['stopOnFailure'] : false; + $arguments['stopOnIncomplete'] = isset($arguments['stopOnIncomplete']) ? $arguments['stopOnIncomplete'] : false; + $arguments['stopOnRisky'] = isset($arguments['stopOnRisky']) ? $arguments['stopOnRisky'] : false; + $arguments['stopOnSkipped'] = isset($arguments['stopOnSkipped']) ? $arguments['stopOnSkipped'] : false; + $arguments['timeoutForSmallTests'] = isset($arguments['timeoutForSmallTests']) ? $arguments['timeoutForSmallTests'] : 1; + $arguments['timeoutForMediumTests'] = isset($arguments['timeoutForMediumTests']) ? $arguments['timeoutForMediumTests'] : 10; + $arguments['timeoutForLargeTests'] = isset($arguments['timeoutForLargeTests']) ? $arguments['timeoutForLargeTests'] : 60; + $arguments['reportUselessTests'] = isset($arguments['reportUselessTests']) ? $arguments['reportUselessTests'] : false; + $arguments['strictCoverage'] = isset($arguments['strictCoverage']) ? $arguments['strictCoverage'] : false; + $arguments['disallowTestOutput'] = isset($arguments['disallowTestOutput']) ? $arguments['disallowTestOutput'] : false; + $arguments['enforceTimeLimit'] = isset($arguments['enforceTimeLimit']) ? $arguments['enforceTimeLimit'] : false; + $arguments['disallowTodoAnnotatedTests'] = isset($arguments['disallowTodoAnnotatedTests']) ? $arguments['disallowTodoAnnotatedTests'] : false; + $arguments['verbose'] = isset($arguments['verbose']) ? $arguments['verbose'] : false; + } + + /** + * @param $extension + * @param string $message + * + * @since Method available since Release 4.7.3 + */ + private function showExtensionNotLoadedWarning($extension, $message = '') + { + if (isset($this->missingExtensions[$extension])) { + return; + } + + $this->write("Warning:\t" . 'The ' . $extension . ' extension is not loaded' . "\n"); + + if (!empty($message)) { + $this->write("\t\t" . $message . "\n"); + } + + $this->missingExtensions[$extension] = true; + } + + /** + * @return PHP_CodeCoverage_Filter + */ + private function getCodeCoverageFilter() + { + $filter = new PHP_CodeCoverage_Filter; + + if (defined('__PHPUNIT_PHAR__')) { + $filter->addFileToBlacklist(__PHPUNIT_PHAR__); + } + + $blacklist = new PHPUnit_Util_Blacklist; + + foreach ($blacklist->getBlacklistedDirectories() as $directory) { + $filter->addDirectoryToBlacklist($directory); + } + + return $filter; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Error handler that converts PHP errors and warnings to exceptions. + * + * @since Class available since Release 4.2.0 + */ +class PHPUnit_Util_Regex +{ + public static function pregMatchSafe($pattern, $subject, $matches = null, $flags = 0, $offset = 0) + { + $handler_terminator = PHPUnit_Util_ErrorHandler::handleErrorOnce(E_WARNING); + $match = preg_match($pattern, $subject, $matches, $flags, $offset); + $handler_terminator(); // cleaning + + return $match; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * String helpers. + * + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Util_String +{ + /** + * Converts a string to UTF-8 encoding. + * + * @param string $string + * + * @return string + */ + public static function convertToUtf8($string) + { + if (!self::isUtf8($string)) { + if (function_exists('mb_convert_encoding')) { + $string = mb_convert_encoding($string, 'UTF-8'); + } else { + $string = utf8_encode($string); + } + } + + return $string; + } + + /** + * Checks a string for UTF-8 encoding. + * + * @param string $string + * + * @return bool + */ + protected static function isUtf8($string) + { + $length = strlen($string); + + for ($i = 0; $i < $length; $i++) { + if (ord($string[$i]) < 0x80) { + $n = 0; + } elseif ((ord($string[$i]) & 0xE0) == 0xC0) { + $n = 1; + } elseif ((ord($string[$i]) & 0xF0) == 0xE0) { + $n = 2; + } elseif ((ord($string[$i]) & 0xF0) == 0xF0) { + $n = 3; + } else { + return false; + } + + for ($j = 0; $j < $n; $j++) { + if ((++$i == $length) || ((ord($string[$i]) & 0xC0) != 0x80)) { + return false; + } + } + } + + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility methods for PHP sub-processes. + * + * @since Class available since Release 3.4.0 + */ +abstract class PHPUnit_Util_PHP +{ + /** + * @return PHPUnit_Util_PHP + * + * @since Method available since Release 3.5.12 + */ + public static function factory() + { + if (DIRECTORY_SEPARATOR == '\\') { + return new PHPUnit_Util_PHP_Windows; + } + + return new PHPUnit_Util_PHP_Default; + } + + /** + * Runs a single test in a separate PHP process. + * + * @param string $job + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_TestResult $result + * + * @throws PHPUnit_Framework_Exception + */ + public function runTestJob($job, PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result) + { + $result->startTest($test); + + $_result = $this->runJob($job); + + $this->processChildResult( + $test, + $result, + $_result['stdout'], + $_result['stderr'] + ); + } + + /** + * Runs a single job (PHP code) using a separate PHP process. + * + * @param string $job + * @param array $settings + * + * @return array + * + * @throws PHPUnit_Framework_Exception + */ + abstract public function runJob($job, array $settings = array()); + + /** + * @param array $settings + * + * @return string + * + * @since Method available since Release 4.0.0 + */ + protected function settingsToParameters(array $settings) + { + $buffer = ''; + + foreach ($settings as $setting) { + $buffer .= ' -d ' . $setting; + } + + return $buffer; + } + + /** + * Processes the TestResult object from an isolated process. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_TestResult $result + * @param string $stdout + * @param string $stderr + * + * @since Method available since Release 3.5.0 + */ + private function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result, $stdout, $stderr) + { + $time = 0; + + if (!empty($stderr)) { + $result->addError( + $test, + new PHPUnit_Framework_Exception(trim($stderr)), + $time + ); + } else { + set_error_handler(function ($errno, $errstr, $errfile, $errline) { + throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); + }); + try { + if (strpos($stdout, "#!/usr/bin/env php\n") === 0) { + $stdout = substr($stdout, 19); + } + + $childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout)); + restore_error_handler(); + } catch (ErrorException $e) { + restore_error_handler(); + $childResult = false; + + $result->addError( + $test, + new PHPUnit_Framework_Exception(trim($stdout), 0, $e), + $time + ); + } + + if ($childResult !== false) { + if (!empty($childResult['output'])) { + $output = $childResult['output']; + } + + $test->setResult($childResult['testResult']); + $test->addToAssertionCount($childResult['numAssertions']); + + $childResult = $childResult['result']; + + if ($result->getCollectCodeCoverageInformation()) { + $result->getCodeCoverage()->merge( + $childResult->getCodeCoverage() + ); + } + + $time = $childResult->time(); + $notImplemented = $childResult->notImplemented(); + $risky = $childResult->risky(); + $skipped = $childResult->skipped(); + $errors = $childResult->errors(); + $failures = $childResult->failures(); + + if (!empty($notImplemented)) { + $result->addError( + $test, + $this->getException($notImplemented[0]), + $time + ); + } elseif (!empty($risky)) { + $result->addError( + $test, + $this->getException($risky[0]), + $time + ); + } elseif (!empty($skipped)) { + $result->addError( + $test, + $this->getException($skipped[0]), + $time + ); + } elseif (!empty($errors)) { + $result->addError( + $test, + $this->getException($errors[0]), + $time + ); + } elseif (!empty($failures)) { + $result->addFailure( + $test, + $this->getException($failures[0]), + $time + ); + } + } + } + + $result->endTest($test, $time); + + if (!empty($output)) { + print $output; + } + } + + /** + * Gets the thrown exception from a PHPUnit_Framework_TestFailure. + * + * @param PHPUnit_Framework_TestFailure $error + * + * @return Exception + * + * @since Method available since Release 3.6.0 + * @see https://github.com/sebastianbergmann/phpunit/issues/74 + */ + private function getException(PHPUnit_Framework_TestFailure $error) + { + $exception = $error->thrownException(); + + if ($exception instanceof __PHP_Incomplete_Class) { + $exceptionArray = array(); + foreach ((array) $exception as $key => $value) { + $key = substr($key, strrpos($key, "\0") + 1); + $exceptionArray[$key] = $value; + } + + $exception = new PHPUnit_Framework_SyntheticError( + sprintf( + '%s: %s', + $exceptionArray['_PHP_Incomplete_Class_Name'], + $exceptionArray['message'] + ), + $exceptionArray['code'], + $exceptionArray['file'], + $exceptionArray['line'], + $exceptionArray['trace'] + ); + } + + return $exception; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Filesystem helpers. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Filesystem +{ + /** + * @var array + */ + protected static $buffer = array(); + + /** + * Maps class names to source file names: + * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php + * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php + * + * @param string $className + * + * @return string + * + * @since Method available since Release 3.4.0 + */ + public static function classNameToFilename($className) + { + return str_replace( + array('_', '\\'), + DIRECTORY_SEPARATOR, + $className + ) . '.php'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (!function_exists('trait_exists')) { + function trait_exists($traitname, $autoload = true) + { + return false; + } +} + +/** + * Test helpers. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Test +{ + const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/'; + const REGEX_TEST_WITH = '/@testWith\s+/'; + const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m'; + const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; + const REGEX_REQUIRES_OS = '/@requires\s+OS\s+(?P.+?)[ \t]*\r?$/m'; + const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^ ]+?))[ \t]*\r?$/m'; + + const UNKNOWN = -1; + const SMALL = 0; + const MEDIUM = 1; + const LARGE = 2; + + private static $annotationCache = array(); + + private static $hookMethods = array(); + + /** + * @param PHPUnit_Framework_Test $test + * @param bool $asString + * + * @return mixed + */ + public static function describe(PHPUnit_Framework_Test $test, $asString = true) + { + if ($asString) { + if ($test instanceof PHPUnit_Framework_SelfDescribing) { + return $test->toString(); + } else { + return get_class($test); + } + } else { + if ($test instanceof PHPUnit_Framework_TestCase) { + return array( + get_class($test), $test->getName() + ); + } elseif ($test instanceof PHPUnit_Framework_SelfDescribing) { + return array('', $test->toString()); + } else { + return array('', get_class($test)); + } + } + } + + /** + * @param string $className + * @param string $methodName + * + * @return array|bool + * + * @throws PHPUnit_Framework_CodeCoverageException + * + * @since Method available since Release 4.0.0 + */ + public static function getLinesToBeCovered($className, $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) { + return false; + } + + return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers'); + } + + /** + * Returns lines of code specified with the @uses annotation. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 4.0.0 + */ + public static function getLinesToBeUsed($className, $methodName) + { + return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses'); + } + + /** + * @param string $className + * @param string $methodName + * @param string $mode + * + * @return array + * + * @throws PHPUnit_Framework_CodeCoverageException + * + * @since Method available since Release 4.2.0 + */ + private static function getLinesToBeCoveredOrUsed($className, $methodName, $mode) + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $classShortcut = null; + + if (!empty($annotations['class'][$mode . 'DefaultClass'])) { + if (count($annotations['class'][$mode . 'DefaultClass']) > 1) { + throw new PHPUnit_Framework_CodeCoverageException( + sprintf( + 'More than one @%sClass annotation in class or interface "%s".', + $mode, + $className + ) + ); + } + + $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0]; + } + + $list = array(); + + if (isset($annotations['class'][$mode])) { + $list = $annotations['class'][$mode]; + } + + if (isset($annotations['method'][$mode])) { + $list = array_merge($list, $annotations['method'][$mode]); + } + + $codeList = array(); + + foreach (array_unique($list) as $element) { + if ($classShortcut && strncmp($element, '::', 2) === 0) { + $element = $classShortcut . $element; + } + + $element = preg_replace('/[\s()]+$/', '', $element); + $element = explode(' ', $element); + $element = $element[0]; + + $codeList = array_merge( + $codeList, + self::resolveElementToReflectionObjects($element) + ); + } + + return self::resolveReflectionObjectsToLines($codeList); + } + + /** + * Returns the requirements for a test. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 3.6.0 + */ + public static function getRequirements($className, $methodName) + { + $reflector = new ReflectionClass($className); + $docComment = $reflector->getDocComment(); + $reflector = new ReflectionMethod($className, $methodName); + $docComment .= "\n" . $reflector->getDocComment(); + $requires = array(); + + if ($count = preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) { + $requires['OS'] = sprintf( + '/%s/i', + addcslashes($matches['value'][$count - 1], '/') + ); + } + if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) { + for ($i = 0; $i < $count; $i++) { + $requires[$matches['name'][$i]] = $matches['value'][$i]; + } + } + + // https://bugs.php.net/bug.php?id=63055 + $matches = array(); + + if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) { + for ($i = 0; $i < $count; $i++) { + $name = $matches['name'][$i] . 's'; + if (!isset($requires[$name])) { + $requires[$name] = array(); + } + $requires[$name][] = $matches['value'][$i]; + } + } + + return $requires; + } + + /** + * Returns the missing requirements for a test. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 4.3.0 + */ + public static function getMissingRequirements($className, $methodName) + { + $required = static::getRequirements($className, $methodName); + $missing = array(); + + if (!empty($required['PHP']) && version_compare(PHP_VERSION, $required['PHP'], '<')) { + $missing[] = sprintf('PHP %s (or later) is required.', $required['PHP']); + } + + if (!empty($required['PHPUnit'])) { + $phpunitVersion = PHPUnit_Runner_Version::id(); + if (version_compare($phpunitVersion, $required['PHPUnit'], '<')) { + $missing[] = sprintf('PHPUnit %s (or later) is required.', $required['PHPUnit']); + } + } + + if (!empty($required['OS']) && !preg_match($required['OS'], PHP_OS)) { + $missing[] = sprintf('Operating system matching %s is required.', $required['OS']); + } + + if (!empty($required['functions'])) { + foreach ($required['functions'] as $function) { + $pieces = explode('::', $function); + if (2 === count($pieces) && method_exists($pieces[0], $pieces[1])) { + continue; + } + if (function_exists($function)) { + continue; + } + $missing[] = sprintf('Function %s is required.', $function); + } + } + + if (!empty($required['extensions'])) { + foreach ($required['extensions'] as $extension) { + if (!extension_loaded($extension)) { + $missing[] = sprintf('Extension %s is required.', $extension); + } + } + } + + return $missing; + } + + /** + * Returns the expected exception for a test. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 3.3.6 + */ + public static function getExpectedException($className, $methodName) + { + $reflector = new ReflectionMethod($className, $methodName); + $docComment = $reflector->getDocComment(); + $docComment = substr($docComment, 3, -2); + + if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $class = $matches[1]; + $code = null; + $message = ''; + $messageRegExp = ''; + + if (isset($matches[2])) { + $message = trim($matches[2]); + } elseif (isset($annotations['method']['expectedExceptionMessage'])) { + $message = self::parseAnnotationContent( + $annotations['method']['expectedExceptionMessage'][0] + ); + } + + if (isset($annotations['method']['expectedExceptionMessageRegExp'])) { + $messageRegExp = self::parseAnnotationContent( + $annotations['method']['expectedExceptionMessageRegExp'][0] + ); + } + + if (isset($matches[3])) { + $code = $matches[3]; + } elseif (isset($annotations['method']['expectedExceptionCode'])) { + $code = self::parseAnnotationContent( + $annotations['method']['expectedExceptionCode'][0] + ); + } + + if (is_numeric($code)) { + $code = (int) $code; + } elseif (is_string($code) && defined($code)) { + $code = (int) constant($code); + } + + return array( + 'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp + ); + } + + return false; + } + + /** + * Parse annotation content to use constant/class constant values + * + * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME + * + * If the constant is not found the string is used as is to ensure maximum BC. + * + * @param string $message + * + * @return string + */ + private static function parseAnnotationContent($message) + { + if (strpos($message, '::') !== false && count(explode('::', $message) == 2)) { + if (defined($message)) { + $message = constant($message); + } + } + + return $message; + } + + /** + * Returns the provided data for a method. + * + * @param string $className + * @param string $methodName + * + * @return array|Iterator when a data provider is specified and exists + * null when no data provider is specified + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.2.0 + */ + public static function getProvidedData($className, $methodName) + { + $reflector = new ReflectionMethod($className, $methodName); + $docComment = $reflector->getDocComment(); + $data = null; + + if ($dataProviderData = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName)) { + $data = $dataProviderData; + } + + if ($testWithData = self::getDataFromTestWithAnnotation($docComment)) { + $data = $testWithData; + } + + if ($data !== null) { + if (is_object($data)) { + $data = iterator_to_array($data); + } + + foreach ($data as $key => $value) { + if (!is_array($value)) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Data set %s is invalid.', + is_int($key) ? '#' . $key : '"' . $key . '"' + ) + ); + } + } + } + + return $data; + } + + /** + * Returns the provided data for a method. + * + * @param string $docComment + * @param string $className + * @param string $methodName + * + * @return array|Iterator when a data provider is specified and exists + * null when no data provider is specified + * + * @throws PHPUnit_Framework_Exception + */ + private static function getDataFromDataProviderAnnotation($docComment, $className, $methodName) + { + if (preg_match(self::REGEX_DATA_PROVIDER, $docComment, $matches)) { + $dataProviderMethodNameNamespace = explode('\\', $matches[1]); + $leaf = explode('::', array_pop($dataProviderMethodNameNamespace)); + $dataProviderMethodName = array_pop($leaf); + + if (!empty($dataProviderMethodNameNamespace)) { + $dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\'; + } else { + $dataProviderMethodNameNamespace = ''; + } + + if (!empty($leaf)) { + $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf); + } else { + $dataProviderClassName = $className; + } + + $dataProviderClass = new ReflectionClass($dataProviderClassName); + $dataProviderMethod = $dataProviderClass->getMethod( + $dataProviderMethodName + ); + + if ($dataProviderMethod->isStatic()) { + $object = null; + } else { + $object = $dataProviderClass->newInstance(); + } + + if ($dataProviderMethod->getNumberOfParameters() == 0) { + $data = $dataProviderMethod->invoke($object); + } else { + $data = $dataProviderMethod->invoke($object, $methodName); + } + + return $data; + } + } + + /** + * @param string $docComment full docComment string + * + * @return array when @testWith annotation is defined + * null when @testWith annotation is omitted + * + * @throws PHPUnit_Framework_Exception when @testWith annotation is defined but cannot be parsed + */ + public static function getDataFromTestWithAnnotation($docComment) + { + $docComment = self::cleanUpMultiLineAnnotation($docComment); + if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) { + $offset = strlen($matches[0][0]) + $matches[0][1]; + $annotationContent = substr($docComment, $offset); + $data = array(); + foreach (explode("\n", $annotationContent) as $candidateRow) { + $candidateRow = trim($candidateRow); + $dataSet = json_decode($candidateRow, true); + if (json_last_error() != JSON_ERROR_NONE) { + break; + } + $data[] = $dataSet; + } + + if (!$data) { + throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.'); + } + + return $data; + } + } + + private static function cleanUpMultiLineAnnotation($docComment) + { + //removing initial ' * ' for docComment + $docComment = preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment); + $docComment = substr($docComment, 0, -1); + $docComment = rtrim($docComment, "\n"); + + return $docComment; + } + + /** + * @param string $className + * @param string $methodName + * + * @return array + * + * @throws ReflectionException + * + * @since Method available since Release 3.4.0 + */ + public static function parseTestMethodAnnotations($className, $methodName = '') + { + if (!isset(self::$annotationCache[$className])) { + $class = new ReflectionClass($className); + self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment()); + } + + if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) { + try { + $method = new ReflectionMethod($className, $methodName); + $annotations = self::parseAnnotations($method->getDocComment()); + } catch (ReflectionException $e) { + $annotations = array(); + } + self::$annotationCache[$className . '::' . $methodName] = $annotations; + } + + return array( + 'class' => self::$annotationCache[$className], + 'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : array() + ); + } + + /** + * @param string $docblock + * + * @return array + * + * @since Method available since Release 3.4.0 + */ + private static function parseAnnotations($docblock) + { + $annotations = array(); + // Strip away the docblock header and footer to ease parsing of one line annotations + $docblock = substr($docblock, 3, -2); + + if (preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docblock, $matches)) { + $numMatches = count($matches[0]); + + for ($i = 0; $i < $numMatches; ++$i) { + $annotations[$matches['name'][$i]][] = $matches['value'][$i]; + } + } + + return $annotations; + } + + /** + * Returns the backup settings for a test. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 3.4.0 + */ + public static function getBackupSettings($className, $methodName) + { + return array( + 'backupGlobals' => self::getBooleanAnnotationSetting( + $className, + $methodName, + 'backupGlobals' + ), + 'backupStaticAttributes' => self::getBooleanAnnotationSetting( + $className, + $methodName, + 'backupStaticAttributes' + ) + ); + } + + /** + * Returns the dependencies for a test class or method. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 3.4.0 + */ + public static function getDependencies($className, $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $dependencies = array(); + + if (isset($annotations['class']['depends'])) { + $dependencies = $annotations['class']['depends']; + } + + if (isset($annotations['method']['depends'])) { + $dependencies = array_merge( + $dependencies, + $annotations['method']['depends'] + ); + } + + return array_unique($dependencies); + } + + /** + * Returns the error handler settings for a test. + * + * @param string $className + * @param string $methodName + * + * @return bool + * + * @since Method available since Release 3.4.0 + */ + public static function getErrorHandlerSettings($className, $methodName) + { + return self::getBooleanAnnotationSetting( + $className, + $methodName, + 'errorHandler' + ); + } + + /** + * Returns the groups for a test class or method. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 3.2.0 + */ + public static function getGroups($className, $methodName = '') + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $groups = array(); + + if (isset($annotations['method']['author'])) { + $groups = $annotations['method']['author']; + } elseif (isset($annotations['class']['author'])) { + $groups = $annotations['class']['author']; + } + + if (isset($annotations['class']['group'])) { + $groups = array_merge($groups, $annotations['class']['group']); + } + + if (isset($annotations['method']['group'])) { + $groups = array_merge($groups, $annotations['method']['group']); + } + + if (isset($annotations['class']['ticket'])) { + $groups = array_merge($groups, $annotations['class']['ticket']); + } + + if (isset($annotations['method']['ticket'])) { + $groups = array_merge($groups, $annotations['method']['ticket']); + } + + foreach (array('method', 'class') as $element) { + foreach (array('small', 'medium', 'large') as $size) { + if (isset($annotations[$element][$size])) { + $groups[] = $size; + break 2; + } + + if (isset($annotations[$element][$size])) { + $groups[] = $size; + break 2; + } + } + } + + return array_unique($groups); + } + + /** + * Returns the size of the test. + * + * @param string $className + * @param string $methodName + * + * @return int + * + * @since Method available since Release 3.6.0 + */ + public static function getSize($className, $methodName) + { + $groups = array_flip(self::getGroups($className, $methodName)); + $size = self::UNKNOWN; + $class = new ReflectionClass($className); + + if (isset($groups['large']) || + (class_exists('PHPUnit_Extensions_Database_TestCase', false) && + $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase')) || + (class_exists('PHPUnit_Extensions_SeleniumTestCase', false) && + $class->isSubclassOf('PHPUnit_Extensions_SeleniumTestCase'))) { + $size = self::LARGE; + } elseif (isset($groups['medium'])) { + $size = self::MEDIUM; + } elseif (isset($groups['small'])) { + $size = self::SMALL; + } + + return $size; + } + + /** + * Returns the tickets for a test class or method. + * + * @param string $className + * @param string $methodName + * + * @return array + * + * @since Method available since Release 3.4.0 + */ + public static function getTickets($className, $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $tickets = array(); + + if (isset($annotations['class']['ticket'])) { + $tickets = $annotations['class']['ticket']; + } + + if (isset($annotations['method']['ticket'])) { + $tickets = array_merge($tickets, $annotations['method']['ticket']); + } + + return array_unique($tickets); + } + + /** + * Returns the process isolation settings for a test. + * + * @param string $className + * @param string $methodName + * + * @return bool + * + * @since Method available since Release 3.4.1 + */ + public static function getProcessIsolationSettings($className, $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + if (isset($annotations['class']['runTestsInSeparateProcesses']) || + isset($annotations['method']['runInSeparateProcess'])) { + return true; + } else { + return false; + } + } + + /** + * Returns the preserve global state settings for a test. + * + * @param string $className + * @param string $methodName + * + * @return bool + * + * @since Method available since Release 3.4.0 + */ + public static function getPreserveGlobalStateSettings($className, $methodName) + { + return self::getBooleanAnnotationSetting( + $className, + $methodName, + 'preserveGlobalState' + ); + } + + /** + * @param string $className + * + * @return array + * + * @since Method available since Release 4.0.8 + */ + public static function getHookMethods($className) + { + if (!class_exists($className, false)) { + return self::emptyHookMethodsArray(); + } + + if (!isset(self::$hookMethods[$className])) { + self::$hookMethods[$className] = self::emptyHookMethodsArray(); + + try { + $class = new ReflectionClass($className); + + foreach ($class->getMethods() as $method) { + if (self::isBeforeClassMethod($method)) { + self::$hookMethods[$className]['beforeClass'][] = $method->getName(); + } + + if (self::isBeforeMethod($method)) { + self::$hookMethods[$className]['before'][] = $method->getName(); + } + + if (self::isAfterMethod($method)) { + self::$hookMethods[$className]['after'][] = $method->getName(); + } + + if (self::isAfterClassMethod($method)) { + self::$hookMethods[$className]['afterClass'][] = $method->getName(); + } + } + } catch (ReflectionException $e) { + } + } + + return self::$hookMethods[$className]; + } + + /** + * @return array + * + * @since Method available since Release 4.0.9 + */ + private static function emptyHookMethodsArray() + { + return array( + 'beforeClass' => array('setUpBeforeClass'), + 'before' => array('setUp'), + 'after' => array('tearDown'), + 'afterClass' => array('tearDownAfterClass') + ); + } + + /** + * @param string $className + * @param string $methodName + * @param string $settingName + * + * @return bool + * + * @since Method available since Release 3.4.0 + */ + private static function getBooleanAnnotationSetting($className, $methodName, $settingName) + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $result = null; + + if (isset($annotations['class'][$settingName])) { + if ($annotations['class'][$settingName][0] == 'enabled') { + $result = true; + } elseif ($annotations['class'][$settingName][0] == 'disabled') { + $result = false; + } + } + + if (isset($annotations['method'][$settingName])) { + if ($annotations['method'][$settingName][0] == 'enabled') { + $result = true; + } elseif ($annotations['method'][$settingName][0] == 'disabled') { + $result = false; + } + } + + return $result; + } + + /** + * @param string $element + * + * @return array + * + * @throws PHPUnit_Framework_InvalidCoversTargetException + * + * @since Method available since Release 4.0.0 + */ + private static function resolveElementToReflectionObjects($element) + { + $codeToCoverList = array(); + + if (strpos($element, '\\') !== false && function_exists($element)) { + $codeToCoverList[] = new ReflectionFunction($element); + } elseif (strpos($element, '::') !== false) { + list($className, $methodName) = explode('::', $element); + + if (isset($methodName[0]) && $methodName[0] == '<') { + $classes = array($className); + + foreach ($classes as $className) { + if (!class_exists($className) && + !interface_exists($className)) { + throw new PHPUnit_Framework_InvalidCoversTargetException( + sprintf( + 'Trying to @cover or @use not existing class or ' . + 'interface "%s".', + $className + ) + ); + } + + $class = new ReflectionClass($className); + $methods = $class->getMethods(); + $inverse = isset($methodName[1]) && $methodName[1] == '!'; + + if (strpos($methodName, 'protected')) { + $visibility = 'isProtected'; + } elseif (strpos($methodName, 'private')) { + $visibility = 'isPrivate'; + } elseif (strpos($methodName, 'public')) { + $visibility = 'isPublic'; + } + + foreach ($methods as $method) { + if ($inverse && !$method->$visibility()) { + $codeToCoverList[] = $method; + } elseif (!$inverse && $method->$visibility()) { + $codeToCoverList[] = $method; + } + } + } + } else { + $classes = array($className); + + foreach ($classes as $className) { + if ($className == '' && function_exists($methodName)) { + $codeToCoverList[] = new ReflectionFunction( + $methodName + ); + } else { + if (!((class_exists($className) || + interface_exists($className) || + trait_exists($className)) && + method_exists($className, $methodName))) { + throw new PHPUnit_Framework_InvalidCoversTargetException( + sprintf( + 'Trying to @cover or @use not existing method "%s::%s".', + $className, + $methodName + ) + ); + } + + $codeToCoverList[] = new ReflectionMethod( + $className, + $methodName + ); + } + } + } + } else { + $extended = false; + + if (strpos($element, '') !== false) { + $element = str_replace('', '', $element); + $extended = true; + } + + $classes = array($element); + + if ($extended) { + $classes = array_merge( + $classes, + class_implements($element), + class_parents($element) + ); + } + + foreach ($classes as $className) { + if (!class_exists($className) && + !interface_exists($className) && + !trait_exists($className)) { + throw new PHPUnit_Framework_InvalidCoversTargetException( + sprintf( + 'Trying to @cover or @use not existing class or ' . + 'interface "%s".', + $className + ) + ); + } + + $codeToCoverList[] = new ReflectionClass($className); + } + } + + return $codeToCoverList; + } + + /** + * @param array $reflectors + * + * @return array + */ + private static function resolveReflectionObjectsToLines(array $reflectors) + { + $result = array(); + + foreach ($reflectors as $reflector) { + $filename = $reflector->getFileName(); + + if (!isset($result[$filename])) { + $result[$filename] = array(); + } + + $result[$filename] = array_unique( + array_merge( + $result[$filename], + range($reflector->getStartLine(), $reflector->getEndLine()) + ) + ); + } + + return $result; + } + + /** + * @param ReflectionMethod $method + * + * @return bool + * + * @since Method available since Release 4.0.8 + */ + private static function isBeforeClassMethod(ReflectionMethod $method) + { + return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false; + } + + /** + * @param ReflectionMethod $method + * + * @return bool + * + * @since Method available since Release 4.0.8 + */ + private static function isBeforeMethod(ReflectionMethod $method) + { + return preg_match('/@before\b/', $method->getDocComment()); + } + + /** + * @param ReflectionMethod $method + * + * @return bool + * + * @since Method available since Release 4.0.8 + */ + private static function isAfterClassMethod(ReflectionMethod $method) + { + return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false; + } + + /** + * @param ReflectionMethod $method + * + * @return bool + * + * @since Method available since Release 4.0.8 + */ + private static function isAfterMethod(ReflectionMethod $method) + { + return preg_match('/@after\b/', $method->getDocComment()); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility methods to load PHP sourcefiles. + * + * @since Class available since Release 2.3.0 + */ +class PHPUnit_Util_Fileloader +{ + /** + * Checks if a PHP sourcefile is readable. + * The sourcefile is loaded through the load() method. + * + * @param string $filename + * + * @return string + * + * @throws PHPUnit_Framework_Exception + */ + public static function checkAndLoad($filename) + { + $includePathFilename = stream_resolve_include_path($filename); + + if (!$includePathFilename || !is_readable($includePathFilename)) { + throw new PHPUnit_Framework_Exception( + sprintf('Cannot open file "%s".' . "\n", $filename) + ); + } + + self::load($includePathFilename); + + return $includePathFilename; + } + + /** + * Loads a PHP sourcefile. + * + * @param string $filename + * + * @return mixed + * + * @since Method available since Release 3.0.0 + */ + public static function load($filename) + { + $oldVariableNames = array_keys(get_defined_vars()); + + include_once $filename; + + $newVariables = get_defined_vars(); + $newVariableNames = array_diff( + array_keys($newVariables), + $oldVariableNames + ); + + foreach ($newVariableNames as $variableName) { + if ($variableName != 'oldVariableNames') { + $GLOBALS[$variableName] = $newVariables[$variableName]; + } + } + + return $filename; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility class for textual type (and value) representation. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Type +{ + public static function isType($type) + { + return in_array( + $type, + array( + 'numeric', + 'integer', + 'int', + 'float', + 'string', + 'boolean', + 'bool', + 'null', + 'array', + 'object', + 'resource', + 'scalar' + ) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wrapper for the PHPUnit XML configuration file. + * + * Example XML configuration file: + * + * + * + * + * + * + * /path/to/files + * /path/to/MyTest.php + * /path/to/files/exclude + * + * + * + * + * + * name + * + * + * name + * + * + * + * + * + * /path/to/files + * /path/to/file + * + * /path/to/files + * /path/to/file + * + * + * + * /path/to/files + * /path/to/file + * + * /path/to/files + * /path/to/file + * + * + * + * + * + * + * + * + * + * Sebastian + * + * + * 22 + * April + * 19.78 + * + * + * MyRelativeFile.php + * MyRelativeDir + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * . + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * @since Class available since Release 3.2.0 + */ +class PHPUnit_Util_Configuration +{ + private static $instances = array(); + + protected $document; + protected $xpath; + protected $filename; + + /** + * Loads a PHPUnit configuration file. + * + * @param string $filename + */ + protected function __construct($filename) + { + $this->filename = $filename; + $this->document = PHPUnit_Util_XML::loadFile($filename, false, true, true); + $this->xpath = new DOMXPath($this->document); + } + + /** + * @since Method available since Release 3.4.0 + */ + final private function __clone() + { + } + + /** + * Returns a PHPUnit configuration object. + * + * @param string $filename + * + * @return PHPUnit_Util_Configuration + * + * @since Method available since Release 3.4.0 + */ + public static function getInstance($filename) + { + $realpath = realpath($filename); + + if ($realpath === false) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Could not read "%s".', + $filename + ) + ); + } + + if (!isset(self::$instances[$realpath])) { + self::$instances[$realpath] = new self($realpath); + } + + return self::$instances[$realpath]; + } + + /** + * Returns the realpath to the configuration file. + * + * @return string + * + * @since Method available since Release 3.6.0 + */ + public function getFilename() + { + return $this->filename; + } + + /** + * Returns the configuration for SUT filtering. + * + * @return array + * + * @since Method available since Release 3.2.1 + */ + public function getFilterConfiguration() + { + $addUncoveredFilesFromWhitelist = true; + $processUncoveredFilesFromWhitelist = false; + + $tmp = $this->xpath->query('filter/whitelist'); + + if ($tmp->length == 1) { + if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { + $addUncoveredFilesFromWhitelist = $this->getBoolean( + (string) $tmp->item(0)->getAttribute( + 'addUncoveredFilesFromWhitelist' + ), + true + ); + } + + if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) { + $processUncoveredFilesFromWhitelist = $this->getBoolean( + (string) $tmp->item(0)->getAttribute( + 'processUncoveredFilesFromWhitelist' + ), + false + ); + } + } + + return array( + 'blacklist' => array( + 'include' => array( + 'directory' => $this->readFilterDirectories( + 'filter/blacklist/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/blacklist/file' + ) + ), + 'exclude' => array( + 'directory' => $this->readFilterDirectories( + 'filter/blacklist/exclude/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/blacklist/exclude/file' + ) + ) + ), + 'whitelist' => array( + 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, + 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, + 'include' => array( + 'directory' => $this->readFilterDirectories( + 'filter/whitelist/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/whitelist/file' + ) + ), + 'exclude' => array( + 'directory' => $this->readFilterDirectories( + 'filter/whitelist/exclude/directory' + ), + 'file' => $this->readFilterFiles( + 'filter/whitelist/exclude/file' + ) + ) + ) + ); + } + + /** + * Returns the configuration for groups. + * + * @return array + * + * @since Method available since Release 3.2.1 + */ + public function getGroupConfiguration() + { + $groups = array( + 'include' => array(), + 'exclude' => array() + ); + + foreach ($this->xpath->query('groups/include/group') as $group) { + $groups['include'][] = (string) $group->textContent; + } + + foreach ($this->xpath->query('groups/exclude/group') as $group) { + $groups['exclude'][] = (string) $group->textContent; + } + + return $groups; + } + + /** + * Returns the configuration for listeners. + * + * @return array + * + * @since Method available since Release 3.4.0 + */ + public function getListenerConfiguration() + { + $result = array(); + + foreach ($this->xpath->query('listeners/listener') as $listener) { + $class = (string) $listener->getAttribute('class'); + $file = ''; + $arguments = array(); + + if ($listener->getAttribute('file')) { + $file = $this->toAbsolutePath( + (string) $listener->getAttribute('file'), + true + ); + } + + foreach ($listener->childNodes as $node) { + if ($node instanceof DOMElement && $node->tagName == 'arguments') { + foreach ($node->childNodes as $argument) { + if ($argument instanceof DOMElement) { + if ($argument->tagName == 'file' || + $argument->tagName == 'directory') { + $arguments[] = $this->toAbsolutePath((string) $argument->textContent); + } else { + $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument); + } + } + } + } + } + + $result[] = array( + 'class' => $class, + 'file' => $file, + 'arguments' => $arguments + ); + } + + return $result; + } + + /** + * Returns the logging configuration. + * + * @return array + */ + public function getLoggingConfiguration() + { + $result = array(); + + foreach ($this->xpath->query('logging/log') as $log) { + $type = (string) $log->getAttribute('type'); + $target = (string) $log->getAttribute('target'); + + if (!$target) { + continue; + } + + $target = $this->toAbsolutePath($target); + + if ($type == 'coverage-html') { + if ($log->hasAttribute('lowUpperBound')) { + $result['lowUpperBound'] = $this->getInteger( + (string) $log->getAttribute('lowUpperBound'), + 50 + ); + } + + if ($log->hasAttribute('highLowerBound')) { + $result['highLowerBound'] = $this->getInteger( + (string) $log->getAttribute('highLowerBound'), + 90 + ); + } + } elseif ($type == 'coverage-crap4j') { + if ($log->hasAttribute('threshold')) { + $result['crap4jThreshold'] = $this->getInteger( + (string) $log->getAttribute('threshold'), + 30 + ); + } + } elseif ($type == 'junit') { + if ($log->hasAttribute('logIncompleteSkipped')) { + $result['logIncompleteSkipped'] = $this->getBoolean( + (string) $log->getAttribute('logIncompleteSkipped'), + false + ); + } + } elseif ($type == 'coverage-text') { + if ($log->hasAttribute('showUncoveredFiles')) { + $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( + (string) $log->getAttribute('showUncoveredFiles'), + false + ); + } + if ($log->hasAttribute('showOnlySummary')) { + $result['coverageTextShowOnlySummary'] = $this->getBoolean( + (string) $log->getAttribute('showOnlySummary'), + false + ); + } + } + + $result[$type] = $target; + } + + return $result; + } + + /** + * Returns the PHP configuration. + * + * @return array + * + * @since Method available since Release 3.2.1 + */ + public function getPHPConfiguration() + { + $result = array( + 'include_path' => array(), + 'ini' => array(), + 'const' => array(), + 'var' => array(), + 'env' => array(), + 'post' => array(), + 'get' => array(), + 'cookie' => array(), + 'server' => array(), + 'files' => array(), + 'request' => array() + ); + + foreach ($this->xpath->query('php/includePath') as $includePath) { + $path = (string) $includePath->textContent; + if ($path) { + $result['include_path'][] = $this->toAbsolutePath($path); + } + } + + foreach ($this->xpath->query('php/ini') as $ini) { + $name = (string) $ini->getAttribute('name'); + $value = (string) $ini->getAttribute('value'); + + $result['ini'][$name] = $value; + } + + foreach ($this->xpath->query('php/const') as $const) { + $name = (string) $const->getAttribute('name'); + $value = (string) $const->getAttribute('value'); + + $result['const'][$name] = $this->getBoolean($value, $value); + } + + foreach (array('var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) { + foreach ($this->xpath->query('php/' . $array) as $var) { + $name = (string) $var->getAttribute('name'); + $value = (string) $var->getAttribute('value'); + + $result[$array][$name] = $this->getBoolean($value, $value); + } + } + + return $result; + } + + /** + * Handles the PHP configuration. + * + * @since Method available since Release 3.2.20 + */ + public function handlePHPConfiguration() + { + $configuration = $this->getPHPConfiguration(); + + if (! empty($configuration['include_path'])) { + ini_set( + 'include_path', + implode(PATH_SEPARATOR, $configuration['include_path']) . + PATH_SEPARATOR . + ini_get('include_path') + ); + } + + foreach ($configuration['ini'] as $name => $value) { + if (defined($value)) { + $value = constant($value); + } + + ini_set($name, $value); + } + + foreach ($configuration['const'] as $name => $value) { + if (!defined($name)) { + define($name, $value); + } + } + + foreach (array('var', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) { + // See https://github.com/sebastianbergmann/phpunit/issues/277 + switch ($array) { + case 'var': + $target = &$GLOBALS; + break; + + case 'server': + $target = &$_SERVER; + break; + + default: + $target = &$GLOBALS['_' . strtoupper($array)]; + break; + } + + foreach ($configuration[$array] as $name => $value) { + $target[$name] = $value; + } + } + + foreach ($configuration['env'] as $name => $value) { + if (false === getenv($name)) { + putenv("{$name}={$value}"); + } + if (!isset($_ENV[$name])) { + $_ENV[$name] = $value; + } + } + } + + /** + * Returns the PHPUnit configuration. + * + * @return array + * + * @since Method available since Release 3.2.14 + */ + public function getPHPUnitConfiguration() + { + $result = array(); + $root = $this->document->documentElement; + + if ($root->hasAttribute('cacheTokens')) { + $result['cacheTokens'] = $this->getBoolean( + (string) $root->getAttribute('cacheTokens'), + false + ); + } + + if ($root->hasAttribute('columns')) { + $columns = (string) $root->getAttribute('columns'); + + if ($columns == 'max') { + $result['columns'] = 'max'; + } else { + $result['columns'] = $this->getInteger($columns, 80); + } + } + + if ($root->hasAttribute('colors')) { + /* only allow boolean for compatibility with previous versions + 'always' only allowed from command line */ + if ($this->getBoolean($root->getAttribute('colors'), false)) { + $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_AUTO; + } else { + $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_NEVER; + } + } + + /* + * Issue #657 + */ + if ($root->hasAttribute('stderr')) { + $result['stderr'] = $this->getBoolean( + (string) $root->getAttribute('stderr'), + false + ); + } + + if ($root->hasAttribute('backupGlobals')) { + $result['backupGlobals'] = $this->getBoolean( + (string) $root->getAttribute('backupGlobals'), + true + ); + } + + if ($root->hasAttribute('backupStaticAttributes')) { + $result['backupStaticAttributes'] = $this->getBoolean( + (string) $root->getAttribute('backupStaticAttributes'), + false + ); + } + + if ($root->getAttribute('bootstrap')) { + $result['bootstrap'] = $this->toAbsolutePath( + (string) $root->getAttribute('bootstrap') + ); + } + + if ($root->hasAttribute('convertErrorsToExceptions')) { + $result['convertErrorsToExceptions'] = $this->getBoolean( + (string) $root->getAttribute('convertErrorsToExceptions'), + true + ); + } + + if ($root->hasAttribute('convertNoticesToExceptions')) { + $result['convertNoticesToExceptions'] = $this->getBoolean( + (string) $root->getAttribute('convertNoticesToExceptions'), + true + ); + } + + if ($root->hasAttribute('convertWarningsToExceptions')) { + $result['convertWarningsToExceptions'] = $this->getBoolean( + (string) $root->getAttribute('convertWarningsToExceptions'), + true + ); + } + + if ($root->hasAttribute('forceCoversAnnotation')) { + $result['forceCoversAnnotation'] = $this->getBoolean( + (string) $root->getAttribute('forceCoversAnnotation'), + false + ); + } + + if ($root->hasAttribute('mapTestClassNameToCoveredClassName')) { + $result['mapTestClassNameToCoveredClassName'] = $this->getBoolean( + (string) $root->getAttribute('mapTestClassNameToCoveredClassName'), + false + ); + } + + if ($root->hasAttribute('processIsolation')) { + $result['processIsolation'] = $this->getBoolean( + (string) $root->getAttribute('processIsolation'), + false + ); + } + + if ($root->hasAttribute('stopOnError')) { + $result['stopOnError'] = $this->getBoolean( + (string) $root->getAttribute('stopOnError'), + false + ); + } + + if ($root->hasAttribute('stopOnFailure')) { + $result['stopOnFailure'] = $this->getBoolean( + (string) $root->getAttribute('stopOnFailure'), + false + ); + } + + if ($root->hasAttribute('stopOnIncomplete')) { + $result['stopOnIncomplete'] = $this->getBoolean( + (string) $root->getAttribute('stopOnIncomplete'), + false + ); + } + + if ($root->hasAttribute('stopOnRisky')) { + $result['stopOnRisky'] = $this->getBoolean( + (string) $root->getAttribute('stopOnRisky'), + false + ); + } + + if ($root->hasAttribute('stopOnSkipped')) { + $result['stopOnSkipped'] = $this->getBoolean( + (string) $root->getAttribute('stopOnSkipped'), + false + ); + } + + if ($root->hasAttribute('testSuiteLoaderClass')) { + $result['testSuiteLoaderClass'] = (string) $root->getAttribute( + 'testSuiteLoaderClass' + ); + } + + if ($root->getAttribute('testSuiteLoaderFile')) { + $result['testSuiteLoaderFile'] = $this->toAbsolutePath( + (string) $root->getAttribute('testSuiteLoaderFile') + ); + } + + if ($root->hasAttribute('printerClass')) { + $result['printerClass'] = (string) $root->getAttribute( + 'printerClass' + ); + } + + if ($root->getAttribute('printerFile')) { + $result['printerFile'] = $this->toAbsolutePath( + (string) $root->getAttribute('printerFile') + ); + } + + if ($root->hasAttribute('timeoutForSmallTests')) { + $result['timeoutForSmallTests'] = $this->getInteger( + (string) $root->getAttribute('timeoutForSmallTests'), + 1 + ); + } + + if ($root->hasAttribute('timeoutForMediumTests')) { + $result['timeoutForMediumTests'] = $this->getInteger( + (string) $root->getAttribute('timeoutForMediumTests'), + 10 + ); + } + + if ($root->hasAttribute('timeoutForLargeTests')) { + $result['timeoutForLargeTests'] = $this->getInteger( + (string) $root->getAttribute('timeoutForLargeTests'), + 60 + ); + } + + if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) { + $result['reportUselessTests'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'), + false + ); + } + + if ($root->hasAttribute('checkForUnintentionallyCoveredCode')) { + $result['strictCoverage'] = $this->getBoolean( + (string) $root->getAttribute('checkForUnintentionallyCoveredCode'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutOutputDuringTests')) { + $result['disallowTestOutput'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutOutputDuringTests'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) { + $result['disallowChangesToGlobalState'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutChangesToGlobalState'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutTestSize')) { + $result['enforceTimeLimit'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutTestSize'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) { + $result['disallowTodoAnnotatedTests'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'), + false + ); + } + + if ($root->hasAttribute('strict')) { + $flag = $this->getBoolean( + (string) $root->getAttribute('strict'), + false + ); + + $result['reportUselessTests'] = $flag; + $result['strictCoverage'] = $flag; + $result['disallowTestOutput'] = $flag; + $result['enforceTimeLimit'] = $flag; + $result['disallowTodoAnnotatedTests'] = $flag; + $result['deprecatedStrictModeSetting'] = true; + } + + if ($root->hasAttribute('verbose')) { + $result['verbose'] = $this->getBoolean( + (string) $root->getAttribute('verbose'), + false + ); + } + + return $result; + } + + /** + * Returns the SeleniumTestCase browser configuration. + * + * @return array + * + * @since Method available since Release 3.2.9 + */ + public function getSeleniumBrowserConfiguration() + { + $result = array(); + + foreach ($this->xpath->query('selenium/browser') as $config) { + $name = (string) $config->getAttribute('name'); + $browser = (string) $config->getAttribute('browser'); + + if ($config->hasAttribute('host')) { + $host = (string) $config->getAttribute('host'); + } else { + $host = 'localhost'; + } + + if ($config->hasAttribute('port')) { + $port = $this->getInteger( + (string) $config->getAttribute('port'), + 4444 + ); + } else { + $port = 4444; + } + + if ($config->hasAttribute('timeout')) { + $timeout = $this->getInteger( + (string) $config->getAttribute('timeout'), + 30000 + ); + } else { + $timeout = 30000; + } + + $result[] = array( + 'name' => $name, + 'browser' => $browser, + 'host' => $host, + 'port' => $port, + 'timeout' => $timeout + ); + } + + return $result; + } + + /** + * Returns the test suite configuration. + * + * @return PHPUnit_Framework_TestSuite + * + * @since Method available since Release 3.2.1 + */ + public function getTestSuiteConfiguration($testSuiteFilter = null) + { + $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); + + if ($testSuiteNodes->length == 0) { + $testSuiteNodes = $this->xpath->query('testsuite'); + } + + if ($testSuiteNodes->length == 1) { + return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); + } + + if ($testSuiteNodes->length > 1) { + $suite = new PHPUnit_Framework_TestSuite; + + foreach ($testSuiteNodes as $testSuiteNode) { + $suite->addTestSuite( + $this->getTestSuite($testSuiteNode, $testSuiteFilter) + ); + } + + return $suite; + } + } + + /** + * @param DOMElement $testSuiteNode + * + * @return PHPUnit_Framework_TestSuite + * + * @since Method available since Release 3.4.0 + */ + protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter = null) + { + if ($testSuiteNode->hasAttribute('name')) { + $suite = new PHPUnit_Framework_TestSuite( + (string) $testSuiteNode->getAttribute('name') + ); + } else { + $suite = new PHPUnit_Framework_TestSuite; + } + + $exclude = array(); + + foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) { + $excludeFile = (string) $excludeNode->textContent; + if ($excludeFile) { + $exclude[] = $this->toAbsolutePath($excludeFile); + } + } + + $fileIteratorFacade = new File_Iterator_Facade; + + foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) { + if ($testSuiteFilter && $directoryNode->parentNode->getAttribute('name') != $testSuiteFilter) { + continue; + } + + $directory = (string) $directoryNode->textContent; + + if (empty($directory)) { + continue; + } + + if ($directoryNode->hasAttribute('phpVersion')) { + $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); + } else { + $phpVersion = PHP_VERSION; + } + + if ($directoryNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = (string) $directoryNode->getAttribute('phpVersionOperator'); + } else { + $phpVersionOperator = '>='; + } + + if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { + continue; + } + + if ($directoryNode->hasAttribute('prefix')) { + $prefix = (string) $directoryNode->getAttribute('prefix'); + } else { + $prefix = ''; + } + + if ($directoryNode->hasAttribute('suffix')) { + $suffix = (string) $directoryNode->getAttribute('suffix'); + } else { + $suffix = 'Test.php'; + } + + $files = $fileIteratorFacade->getFilesAsArray( + $this->toAbsolutePath($directory), + $suffix, + $prefix, + $exclude + ); + $suite->addTestFiles($files); + } + + foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) { + if ($testSuiteFilter && $fileNode->parentNode->getAttribute('name') != $testSuiteFilter) { + continue; + } + + $file = (string) $fileNode->textContent; + + if (empty($file)) { + continue; + } + + // Get the absolute path to the file + $file = $fileIteratorFacade->getFilesAsArray( + $this->toAbsolutePath($file) + ); + + if (!isset($file[0])) { + continue; + } + + $file = $file[0]; + + if ($fileNode->hasAttribute('phpVersion')) { + $phpVersion = (string) $fileNode->getAttribute('phpVersion'); + } else { + $phpVersion = PHP_VERSION; + } + + if ($fileNode->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = (string) $fileNode->getAttribute('phpVersionOperator'); + } else { + $phpVersionOperator = '>='; + } + + if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { + continue; + } + + $suite->addTestFile($file); + } + + return $suite; + } + + /** + * @param string $value + * @param bool $default + * + * @return bool + * + * @since Method available since Release 3.2.3 + */ + protected function getBoolean($value, $default) + { + if (strtolower($value) == 'false') { + return false; + } elseif (strtolower($value) == 'true') { + return true; + } + + return $default; + } + + /** + * @param string $value + * @param bool $default + * + * @return bool + * + * @since Method available since Release 3.6.0 + */ + protected function getInteger($value, $default) + { + if (is_numeric($value)) { + return (int) $value; + } + + return $default; + } + + /** + * @param string $query + * + * @return array + * + * @since Method available since Release 3.2.3 + */ + protected function readFilterDirectories($query) + { + $directories = array(); + + foreach ($this->xpath->query($query) as $directory) { + $directoryPath = (string) $directory->textContent; + + if (!$directoryPath) { + continue; + } + + if ($directory->hasAttribute('prefix')) { + $prefix = (string) $directory->getAttribute('prefix'); + } else { + $prefix = ''; + } + + if ($directory->hasAttribute('suffix')) { + $suffix = (string) $directory->getAttribute('suffix'); + } else { + $suffix = '.php'; + } + + if ($directory->hasAttribute('group')) { + $group = (string) $directory->getAttribute('group'); + } else { + $group = 'DEFAULT'; + } + + $directories[] = array( + 'path' => $this->toAbsolutePath($directoryPath), + 'prefix' => $prefix, + 'suffix' => $suffix, + 'group' => $group + ); + } + + return $directories; + } + + /** + * @param string $query + * + * @return array + * + * @since Method available since Release 3.2.3 + */ + protected function readFilterFiles($query) + { + $files = array(); + + foreach ($this->xpath->query($query) as $file) { + $filePath = (string) $file->textContent; + + if ($filePath) { + $files[] = $this->toAbsolutePath($filePath); + } + } + + return $files; + } + + /** + * @param string $path + * @param bool $useIncludePath + * + * @return string + * + * @since Method available since Release 3.5.0 + */ + protected function toAbsolutePath($path, $useIncludePath = false) + { + $path = trim($path); + + if ($path[0] === '/') { + return $path; + } + + // Matches the following on Windows: + // - \\NetworkComputer\Path + // - \\.\D: + // - \\.\c: + // - C:\Windows + // - C:\windows + // - C:/windows + // - c:/windows + if (defined('PHP_WINDOWS_VERSION_BUILD') && + ($path[0] === '\\' || + (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))))) { + return $path; + } + + // Stream + if (strpos($path, '://') !== false) { + return $path; + } + + $file = dirname($this->filename) . DIRECTORY_SEPARATOR . $path; + + if ($useIncludePath && !file_exists($file)) { + $includePathFile = stream_resolve_include_path($path); + + if ($includePathFile) { + $file = $includePathFile; + } + } + + return $file; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Factory for PHPUnit_Framework_Exception objects that are used to describe + * invalid arguments passed to a function or method. + * + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Util_InvalidArgumentHelper +{ + /** + * @param int $argument + * @param string $type + * @param mixed $value + * + * @return PHPUnit_Framework_Exception + */ + public static function factory($argument, $type, $value = null) + { + $stack = debug_backtrace(false); + + return new PHPUnit_Framework_Exception( + sprintf( + 'Argument #%d%sof %s::%s() must be a %s', + $argument, + $value !== null ? ' (' . gettype($value) . '#' . $value . ')' : ' (No Value) ', + $stack[1]['class'], + $stack[1]['function'], + $type + ) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility class for code filtering. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Util_Filter +{ + /** + * Filters stack frames from PHPUnit classes. + * + * @param Exception $e + * @param bool $asString + * + * @return string + */ + public static function getFilteredStacktrace(Exception $e, $asString = true) + { + $prefix = false; + $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); + + if (defined('__PHPUNIT_PHAR_ROOT__')) { + $prefix = __PHPUNIT_PHAR_ROOT__; + } + + if ($asString === true) { + $filteredStacktrace = ''; + } else { + $filteredStacktrace = array(); + } + + if ($e instanceof PHPUnit_Framework_SyntheticError) { + $eTrace = $e->getSyntheticTrace(); + $eFile = $e->getSyntheticFile(); + $eLine = $e->getSyntheticLine(); + } elseif ($e instanceof PHPUnit_Framework_Exception) { + $eTrace = $e->getSerializableTrace(); + $eFile = $e->getFile(); + $eLine = $e->getLine(); + } else { + if ($e->getPrevious()) { + $e = $e->getPrevious(); + } + $eTrace = $e->getTrace(); + $eFile = $e->getFile(); + $eLine = $e->getLine(); + } + + if (!self::frameExists($eTrace, $eFile, $eLine)) { + array_unshift( + $eTrace, + array('file' => $eFile, 'line' => $eLine) + ); + } + + $blacklist = new PHPUnit_Util_Blacklist; + + foreach ($eTrace as $frame) { + if (isset($frame['file']) && is_file($frame['file']) && + !$blacklist->isBlacklisted($frame['file']) && + ($prefix === false || strpos($frame['file'], $prefix) !== 0) && + $frame['file'] !== $script) { + if ($asString === true) { + $filteredStacktrace .= sprintf( + "%s:%s\n", + $frame['file'], + isset($frame['line']) ? $frame['line'] : '?' + ); + } else { + $filteredStacktrace[] = $frame; + } + } + } + + return $filteredStacktrace; + } + + /** + * @param array $trace + * @param string $file + * @param int $line + * + * @return bool + * + * @since Method available since Release 3.3.2 + */ + private static function frameExists(array $trace, $file, $line) + { + foreach ($trace as $frame) { + if (isset($frame['file']) && $frame['file'] == $file && + isset($frame['line']) && $frame['line'] == $line) { + return true; + } + } + + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestListener that generates JSON messages. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Log_JSON extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var string + */ + protected $currentTestSuiteName = ''; + + /** + * @var string + */ + protected $currentTestName = ''; + + /** + * @var bool + */ + protected $currentTestPass = true; + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, false), + $e->getMessage(), + $test + ); + + $this->currentTestPass = false; + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->writeCase( + 'fail', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, false), + $e->getMessage(), + $test + ); + + $this->currentTestPass = false; + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, false), + 'Incomplete Test: ' . $e->getMessage(), + $test + ); + + $this->currentTestPass = false; + } + + /** + * Risky test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 4.0.0 + */ + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, false), + 'Risky Test: ' . $e->getMessage(), + $test + ); + + $this->currentTestPass = false; + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeCase( + 'error', + $time, + PHPUnit_Util_Filter::getFilteredStacktrace($e, false), + 'Skipped Test: ' . $e->getMessage(), + $test + ); + + $this->currentTestPass = false; + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->currentTestSuiteName = $suite->getName(); + $this->currentTestName = ''; + + $this->write( + array( + 'event' => 'suiteStart', + 'suite' => $this->currentTestSuiteName, + 'tests' => count($suite) + ) + ); + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->currentTestSuiteName = ''; + $this->currentTestName = ''; + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + $this->currentTestName = PHPUnit_Util_Test::describe($test); + $this->currentTestPass = true; + + $this->write( + array( + 'event' => 'testStart', + 'suite' => $this->currentTestSuiteName, + 'test' => $this->currentTestName + ) + ); + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if ($this->currentTestPass) { + $this->writeCase('pass', $time, array(), '', $test); + } + } + + /** + * @param string $status + * @param float $time + * @param array $trace + * @param string $message + * @param PHPUnit_Framework_TestCase|null $test + */ + protected function writeCase($status, $time, array $trace = array(), $message = '', $test = null) + { + $output = ''; + // take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput + if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) { + $output = $test->getActualOutput(); + } + $this->write( + array( + 'event' => 'test', + 'suite' => $this->currentTestSuiteName, + 'test' => $this->currentTestName, + 'status' => $status, + 'time' => $time, + 'trace' => $trace, + 'message' => PHPUnit_Util_String::convertToUtf8($message), + 'output' => $output, + ) + ); + } + + /** + * @param string $buffer + */ + public function write($buffer) + { + array_walk_recursive($buffer, function (&$input) { + if (is_string($input)) { + $input = PHPUnit_Util_String::convertToUtf8($input); + } + }); + + $flags = 0; + + if (defined('JSON_PRETTY_PRINT')) { + $flags |= JSON_PRETTY_PRINT; + } + + parent::write(json_encode($buffer, $flags)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestListener that generates a logfile of the + * test execution using the Test Anything Protocol (TAP). + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Log_TAP extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var int + */ + protected $testNumber = 0; + + /** + * @var int + */ + protected $testSuiteLevel = 0; + + /** + * @var bool + */ + protected $testSuccessful = true; + + /** + * Constructor. + * + * @param mixed $out + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.3.4 + */ + public function __construct($out = null) + { + parent::__construct($out); + $this->write("TAP version 13\n"); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeNotOk($test, 'Error'); + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->writeNotOk($test, 'Failure'); + + $message = explode( + "\n", + PHPUnit_Framework_TestFailure::exceptionToString($e) + ); + + $diagnostic = array( + 'message' => $message[0], + 'severity' => 'fail' + ); + + if ($e instanceof PHPUnit_Framework_ExpectationFailedException) { + $cf = $e->getComparisonFailure(); + + if ($cf !== null) { + $diagnostic['data'] = array( + 'got' => $cf->getActual(), + 'expected' => $cf->getExpected() + ); + } + } + + $yaml = new Symfony\Component\Yaml\Dumper; + + $this->write( + sprintf( + " ---\n%s ...\n", + $yaml->dump($diagnostic, 2, 2) + ) + ); + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->writeNotOk($test, '', 'TODO Incomplete Test'); + } + + /** + * Risky test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 4.0.0 + */ + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->write( + sprintf( + "ok %d - # RISKY%s\n", + $this->testNumber, + $e->getMessage() != '' ? ' ' . $e->getMessage() : '' + ) + ); + + $this->testSuccessful = false; + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->write( + sprintf( + "ok %d - # SKIP%s\n", + $this->testNumber, + $e->getMessage() != '' ? ' ' . $e->getMessage() : '' + ) + ); + + $this->testSuccessful = false; + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->testSuiteLevel++; + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->testSuiteLevel--; + + if ($this->testSuiteLevel == 0) { + $this->write(sprintf("1..%d\n", $this->testNumber)); + } + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + $this->testNumber++; + $this->testSuccessful = true; + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if ($this->testSuccessful === true) { + $this->write( + sprintf( + "ok %d - %s\n", + $this->testNumber, + PHPUnit_Util_Test::describe($test) + ) + ); + } + + $this->writeDiagnostics($test); + } + + /** + * @param PHPUnit_Framework_Test $test + * @param string $prefix + * @param string $directive + */ + protected function writeNotOk(PHPUnit_Framework_Test $test, $prefix = '', $directive = '') + { + $this->write( + sprintf( + "not ok %d - %s%s%s\n", + $this->testNumber, + $prefix != '' ? $prefix . ': ' : '', + PHPUnit_Util_Test::describe($test), + $directive != '' ? ' # ' . $directive : '' + ) + ); + + $this->testSuccessful = false; + } + + /** + * @param PHPUnit_Framework_Test $test + */ + private function writeDiagnostics(PHPUnit_Framework_Test $test) + { + if (!$test instanceof PHPUnit_Framework_TestCase) { + return; + } + + if (!$test->hasOutput()) { + return; + } + + foreach (explode("\n", trim($test->getActualOutput())) as $line) { + $this->write( + sprintf( + "# %s\n", + $line + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestListener that generates a logfile of the test execution in XML markup. + * + * The XML markup used is the same as the one that is used by the JUnit Ant task. + * + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_Log_JUnit extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * @var DOMElement + */ + protected $root; + + /** + * @var bool + */ + protected $logIncompleteSkipped = false; + + /** + * @var bool + */ + protected $writeDocument = true; + + /** + * @var DOMElement[] + */ + protected $testSuites = array(); + + /** + * @var int[] + */ + protected $testSuiteTests = array(0); + + /** + * @var int[] + */ + protected $testSuiteAssertions = array(0); + + /** + * @var int[] + */ + protected $testSuiteErrors = array(0); + + /** + * @var int[] + */ + protected $testSuiteFailures = array(0); + + /** + * @var int[] + */ + protected $testSuiteTimes = array(0); + + /** + * @var int + */ + protected $testSuiteLevel = 0; + + /** + * @var DOMElement + */ + protected $currentTestCase = null; + + /** + * @var bool + */ + protected $attachCurrentTestCase = true; + + /** + * Constructor. + * + * @param mixed $out + * @param bool $logIncompleteSkipped + */ + public function __construct($out = null, $logIncompleteSkipped = false) + { + $this->document = new DOMDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + + $this->root = $this->document->createElement('testsuites'); + $this->document->appendChild($this->root); + + parent::__construct($out); + + $this->logIncompleteSkipped = $logIncompleteSkipped; + } + + /** + * Flush buffer and close output. + */ + public function flush() + { + if ($this->writeDocument === true) { + $this->write($this->getXML()); + } + + parent::flush(); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->currentTestCase === null) { + return; + } + + if ($test instanceof PHPUnit_Framework_SelfDescribing) { + $buffer = $test->toString() . "\n"; + } else { + $buffer = ''; + } + + $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . + "\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e); + + $error = $this->document->createElement( + 'error', + PHPUnit_Util_XML::prepareString($buffer) + ); + + $error->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + if ($this->currentTestCase === null) { + return; + } + + if ($test instanceof PHPUnit_Framework_SelfDescribing) { + $buffer = $test->toString() . "\n"; + } else { + $buffer = ''; + } + + $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . + "\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e); + + $failure = $this->document->createElement( + 'failure', + PHPUnit_Util_XML::prepareString($buffer) + ); + + $failure->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($failure); + + $this->testSuiteFailures[$this->testSuiteLevel]++; + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->logIncompleteSkipped && $this->currentTestCase !== null) { + $error = $this->document->createElement( + 'error', + PHPUnit_Util_XML::prepareString( + "Incomplete Test\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e) + ) + ); + + $error->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } else { + $this->attachCurrentTestCase = false; + } + } + + /** + * Risky test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 4.0.0 + */ + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->logIncompleteSkipped && $this->currentTestCase !== null) { + $error = $this->document->createElement( + 'error', + PHPUnit_Util_XML::prepareString( + "Risky Test\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e) + ) + ); + + $error->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } else { + $this->attachCurrentTestCase = false; + } + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($this->logIncompleteSkipped && $this->currentTestCase !== null) { + $error = $this->document->createElement( + 'error', + PHPUnit_Util_XML::prepareString( + "Skipped Test\n" . + PHPUnit_Util_Filter::getFilteredStacktrace($e) + ) + ); + + $error->setAttribute('type', get_class($e)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } else { + $this->attachCurrentTestCase = false; + } + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $testSuite = $this->document->createElement('testsuite'); + $testSuite->setAttribute('name', $suite->getName()); + + if (class_exists($suite->getName(), false)) { + try { + $class = new ReflectionClass($suite->getName()); + + $testSuite->setAttribute('file', $class->getFileName()); + } catch (ReflectionException $e) { + } + } + + if ($this->testSuiteLevel > 0) { + $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); + } else { + $this->root->appendChild($testSuite); + } + + $this->testSuiteLevel++; + $this->testSuites[$this->testSuiteLevel] = $testSuite; + $this->testSuiteTests[$this->testSuiteLevel] = 0; + $this->testSuiteAssertions[$this->testSuiteLevel] = 0; + $this->testSuiteErrors[$this->testSuiteLevel] = 0; + $this->testSuiteFailures[$this->testSuiteLevel] = 0; + $this->testSuiteTimes[$this->testSuiteLevel] = 0; + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'tests', + $this->testSuiteTests[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'assertions', + $this->testSuiteAssertions[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'failures', + $this->testSuiteFailures[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'errors', + $this->testSuiteErrors[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'time', + sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]) + ); + + if ($this->testSuiteLevel > 1) { + $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel]; + $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel]; + $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel]; + $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel]; + $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; + } + + $this->testSuiteLevel--; + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + $testCase = $this->document->createElement('testcase'); + $testCase->setAttribute('name', $test->getName()); + + if ($test instanceof PHPUnit_Framework_TestCase) { + $class = new ReflectionClass($test); + $methodName = $test->getName(); + + if ($class->hasMethod($methodName)) { + $method = $class->getMethod($test->getName()); + + $testCase->setAttribute('class', $class->getName()); + $testCase->setAttribute('file', $class->getFileName()); + $testCase->setAttribute('line', $method->getStartLine()); + } + } + + $this->currentTestCase = $testCase; + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if ($this->attachCurrentTestCase) { + if ($test instanceof PHPUnit_Framework_TestCase) { + $numAssertions = $test->getNumAssertions(); + $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; + + $this->currentTestCase->setAttribute( + 'assertions', + $numAssertions + ); + } + + $this->currentTestCase->setAttribute( + 'time', + sprintf('%F', $time) + ); + + $this->testSuites[$this->testSuiteLevel]->appendChild( + $this->currentTestCase + ); + + $this->testSuiteTests[$this->testSuiteLevel]++; + $this->testSuiteTimes[$this->testSuiteLevel] += $time; + + if (method_exists($test, 'hasOutput') && $test->hasOutput()) { + $systemOut = $this->document->createElement('system-out'); + $systemOut->appendChild( + $this->document->createTextNode($test->getActualOutput()) + ); + $this->currentTestCase->appendChild($systemOut); + } + } + + $this->attachCurrentTestCase = true; + $this->currentTestCase = null; + } + + /** + * Returns the XML as a string. + * + * @return string + * + * @since Method available since Release 2.2.0 + */ + public function getXML() + { + return $this->document->saveXML(); + } + + /** + * Enables or disables the writing of the document + * in flush(). + * + * This is a "hack" needed for the integration of + * PHPUnit with Phing. + * + * @return string + * + * @since Method available since Release 2.2.0 + */ + public function setWriteDocument($flag) + { + if (is_bool($flag)) { + $this->writeDocument = $flag; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Command-line options parsing class. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Util_Getopt +{ + public static function getopt(array $args, $short_options, $long_options = null) + { + if (empty($args)) { + return array(array(), array()); + } + + $opts = array(); + $non_opts = array(); + + if ($long_options) { + sort($long_options); + } + + if (isset($args[0][0]) && $args[0][0] != '-') { + array_shift($args); + } + + reset($args); + array_map('trim', $args); + + while (list($i, $arg) = each($args)) { + if ($arg == '') { + continue; + } + + if ($arg == '--') { + $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); + break; + } + + if ($arg[0] != '-' || + (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) { + $non_opts[] = $args[$i]; + continue; + } elseif (strlen($arg) > 1 && $arg[1] == '-') { + self::parseLongOption( + substr($arg, 2), + $long_options, + $opts, + $args + ); + } else { + self::parseShortOption( + substr($arg, 1), + $short_options, + $opts, + $args + ); + } + } + + return array($opts, $non_opts); + } + + protected static function parseShortOption($arg, $short_options, &$opts, &$args) + { + $argLen = strlen($arg); + + for ($i = 0; $i < $argLen; $i++) { + $opt = $arg[$i]; + $opt_arg = null; + + if (($spec = strstr($short_options, $opt)) === false || + $arg[$i] == ':') { + throw new PHPUnit_Framework_Exception( + "unrecognized option -- $opt" + ); + } + + if (strlen($spec) > 1 && $spec[1] == ':') { + if (strlen($spec) > 2 && $spec[2] == ':') { + if ($i + 1 < $argLen) { + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } + } else { + if ($i + 1 < $argLen) { + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } elseif (list(, $opt_arg) = each($args)) { + } else { + throw new PHPUnit_Framework_Exception( + "option requires an argument -- $opt" + ); + } + } + } + + $opts[] = array($opt, $opt_arg); + } + } + + protected static function parseLongOption($arg, $long_options, &$opts, &$args) + { + $count = count($long_options); + $list = explode('=', $arg); + $opt = $list[0]; + $opt_arg = null; + + if (count($list) > 1) { + $opt_arg = $list[1]; + } + + $opt_len = strlen($opt); + + for ($i = 0; $i < $count; $i++) { + $long_opt = $long_options[$i]; + $opt_start = substr($long_opt, 0, $opt_len); + + if ($opt_start != $opt) { + continue; + } + + $opt_rest = substr($long_opt, $opt_len); + + if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < $count && + $opt == substr($long_options[$i+1], 0, $opt_len)) { + throw new PHPUnit_Framework_Exception( + "option --$opt is ambiguous" + ); + } + + if (substr($long_opt, -1) == '=') { + if (substr($long_opt, -2) != '==') { + if (!strlen($opt_arg) && + !(list(, $opt_arg) = each($args))) { + throw new PHPUnit_Framework_Exception( + "option --$opt requires an argument" + ); + } + } + } elseif ($opt_arg) { + throw new PHPUnit_Framework_Exception( + "option --$opt doesn't allow an argument" + ); + } + + $full_option = '--' . preg_replace('/={1,2}$/', '', $long_opt); + $opts[] = array($full_option, $opt_arg); + + return; + } + + throw new PHPUnit_Framework_Exception("unrecognized option --$opt"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SebastianBergmann\Environment\Runtime; + +/** + * Windows utility for PHP sub-processes. + * + * @since Class available since Release 3.5.12 + */ +class PHPUnit_Util_PHP_Windows extends PHPUnit_Util_PHP_Default +{ + /** + * @var string + */ + private $tempFile; + + /** + * {@inheritdoc} + * + * Reading from STDOUT or STDERR hangs forever on Windows if the output is + * too large. + * + * @see https://bugs.php.net/bug.php?id=51800 + */ + public function runJob($job, array $settings = array()) + { + $runtime = new Runtime; + + if (false === $stdout_handle = tmpfile()) { + throw new PHPUnit_Framework_Exception( + 'A temporary file could not be created; verify that your TEMP environment variable is writable' + ); + } + + $process = proc_open( + $runtime->getBinary() . $this->settingsToParameters($settings), + array( + 0 => array('pipe', 'r'), + 1 => $stdout_handle, + 2 => array('pipe', 'w') + ), + $pipes + ); + + if (!is_resource($process)) { + throw new PHPUnit_Framework_Exception( + 'Unable to spawn worker process' + ); + } + + $this->process($pipes[0], $job); + fclose($pipes[0]); + + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[2]); + + proc_close($process); + + rewind($stdout_handle); + $stdout = stream_get_contents($stdout_handle); + fclose($stdout_handle); + + $this->cleanup(); + + return array('stdout' => $stdout, 'stderr' => $stderr); + } + + /** + * @param resource $pipe + * @param string $job + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.5.12 + */ + protected function process($pipe, $job) + { + if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'PHPUnit')) || + file_put_contents($this->tempFile, $job) === false) { + throw new PHPUnit_Framework_Exception( + 'Unable to write temporary file' + ); + } + + fwrite( + $pipe, + 'tempFile, true) . '; ?>' + ); + } + + /** + * @since Method available since Release 3.5.12 + */ + protected function cleanup() + { + unlink($this->tempFile); + } +} +' . file_get_contents('php://input')); +setCodeCoverage( + new PHP_CodeCoverage( + null, + unserialize('{codeCoverageFilter}') + ) + ); + } + + $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); + $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); + $result->beStrictAboutTestSize({isStrictAboutTestSize}); + $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); + + $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(TRUE); + + ob_end_clean(); + $test->run($result); + $output = ''; + if (!$test->hasExpectationOnOutput()) { + $output = $test->getActualOutput(); + } + + rewind(STDOUT); + if ($stdout = stream_get_contents(STDOUT)) { + $output = $stdout . $output; + } + + print serialize( + array( + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ) + ); +} + +$configurationFilePath = '{configurationFilePath}'; + +if ('' !== $configurationFilePath) { + $configuration = PHPUnit_Util_Configuration::getInstance($configurationFilePath); + $configuration->handlePHPConfiguration(); + unset($configuration); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext) +{ + return true; +} + +set_error_handler("__phpunit_error_handler"); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SebastianBergmann\Environment\Runtime; + +/** + * Default utility for PHP sub-processes. + * + * @since Class available since Release 3.5.12 + */ +class PHPUnit_Util_PHP_Default extends PHPUnit_Util_PHP +{ + /** + * Runs a single job (PHP code) using a separate PHP process. + * + * @param string $job + * @param array $settings + * + * @return array + * + * @throws PHPUnit_Framework_Exception + */ + public function runJob($job, array $settings = array()) + { + $runtime = new Runtime; + $runtime = $runtime->getBinary() . $this->settingsToParameters($settings); + + if ('phpdbg' === PHP_SAPI) { + $runtime .= ' -qrr ' . escapeshellarg(__DIR__ . '/eval-stdin.php'); + } + + $process = proc_open( + $runtime, + array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ), + $pipes + ); + + if (!is_resource($process)) { + throw new PHPUnit_Framework_Exception( + 'Unable to spawn worker process' + ); + } + + $this->process($pipes[0], $job); + fclose($pipes[0]); + + $stdout = stream_get_contents($pipes[1]); + fclose($pipes[1]); + + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[2]); + + proc_close($process); + $this->cleanup(); + + return array('stdout' => $stdout, 'stderr' => $stderr); + } + + /** + * @param resource $pipe + * @param string $job + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.5.12 + */ + protected function process($pipe, $job) + { + fwrite($pipe, $job); + } + + /** + * @since Method available since Release 3.5.12 + */ + protected function cleanup() + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Util_GlobalState +{ + /** + * @var array + */ + protected static $superGlobalArrays = array( + '_ENV', + '_POST', + '_GET', + '_COOKIE', + '_SERVER', + '_FILES', + '_REQUEST' + ); + + /** + * @var array + */ + protected static $superGlobalArraysLong = array( + 'HTTP_ENV_VARS', + 'HTTP_POST_VARS', + 'HTTP_GET_VARS', + 'HTTP_COOKIE_VARS', + 'HTTP_SERVER_VARS', + 'HTTP_POST_FILES' + ); + + public static function getIncludedFilesAsString() + { + return static::processIncludedFilesAsString(get_included_files()); + } + + public static function processIncludedFilesAsString(array $files) + { + $blacklist = new PHPUnit_Util_Blacklist; + $prefix = false; + $result = ''; + + if (defined('__PHPUNIT_PHAR__')) { + $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; + } + + for ($i = count($files) - 1; $i > 0; $i--) { + $file = $files[$i]; + + if ($prefix !== false && strpos($file, $prefix) === 0) { + continue; + } + + // Skip virtual file system protocols + if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { + continue; + } + + if (!$blacklist->isBlacklisted($file) && is_file($file)) { + $result = 'require_once \'' . $file . "';\n" . $result; + } + } + + return $result; + } + + public static function getIniSettingsAsString() + { + $result = ''; + $iniSettings = ini_get_all(null, false); + + foreach ($iniSettings as $key => $value) { + $result .= sprintf( + '@ini_set(%s, %s);' . "\n", + self::exportVariable($key), + self::exportVariable($value) + ); + } + + return $result; + } + + public static function getConstantsAsString() + { + $constants = get_defined_constants(true); + $result = ''; + + if (isset($constants['user'])) { + foreach ($constants['user'] as $name => $value) { + $result .= sprintf( + 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", + $name, + $name, + self::exportVariable($value) + ); + } + } + + return $result; + } + + public static function getGlobalsAsString() + { + $result = ''; + $superGlobalArrays = self::getSuperGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + if (isset($GLOBALS[$superGlobalArray]) && + is_array($GLOBALS[$superGlobalArray])) { + foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) { + if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { + continue; + } + + $result .= sprintf( + '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", + $superGlobalArray, + $key, + self::exportVariable($GLOBALS[$superGlobalArray][$key]) + ); + } + } + } + + $blacklist = $superGlobalArrays; + $blacklist[] = 'GLOBALS'; + + foreach (array_keys($GLOBALS) as $key) { + if (!in_array($key, $blacklist) && !$GLOBALS[$key] instanceof Closure) { + $result .= sprintf( + '$GLOBALS[\'%s\'] = %s;' . "\n", + $key, + self::exportVariable($GLOBALS[$key]) + ); + } + } + + return $result; + } + + protected static function getSuperGlobalArrays() + { + if (ini_get('register_long_arrays') == '1') { + return array_merge( + self::$superGlobalArrays, + self::$superGlobalArraysLong + ); + } else { + return self::$superGlobalArrays; + } + } + + protected static function exportVariable($variable) + { + if (is_scalar($variable) || is_null($variable) || + (is_array($variable) && self::arrayOnlyContainsScalars($variable))) { + return var_export($variable, true); + } + + return 'unserialize(' . + var_export(serialize($variable), true) . + ')'; + } + + protected static function arrayOnlyContainsScalars(array $array) + { + $result = true; + + foreach ($array as $element) { + if (is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!is_scalar($element) && !is_null($element)) { + $result = false; + } + + if ($result === false) { + break; + } + } + + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Iterator for test suites. + * + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Util_TestSuiteIterator implements RecursiveIterator +{ + /** + * @var int + */ + protected $position; + + /** + * @var PHPUnit_Framework_Test[] + */ + protected $tests; + + /** + * @param PHPUnit_Framework_TestSuite $testSuite + */ + public function __construct(PHPUnit_Framework_TestSuite $testSuite) + { + $this->tests = $testSuite->tests(); + } + + /** + * Rewinds the Iterator to the first element. + */ + public function rewind() + { + $this->position = 0; + } + + /** + * Checks if there is a current element after calls to rewind() or next(). + * + * @return bool + */ + public function valid() + { + return $this->position < count($this->tests); + } + + /** + * Returns the key of the current element. + * + * @return int + */ + public function key() + { + return $this->position; + } + + /** + * Returns the current element. + * + * @return PHPUnit_Framework_Test + */ + public function current() + { + return $this->valid() ? $this->tests[$this->position] : null; + } + + /** + * Moves forward to next element. + */ + public function next() + { + $this->position++; + } + + /** + * Returns the sub iterator for the current element. + * + * @return PHPUnit_Util_TestSuiteIterator + */ + public function getChildren() + { + return new self( + $this->tests[$this->position] + ); + } + + /** + * Checks whether the current element has children. + * + * @return bool + */ + public function hasChildren() + { + return $this->tests[$this->position] instanceof PHPUnit_Framework_TestSuite; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// Workaround for http://bugs.php.net/bug.php?id=47987, +// see https://github.com/sebastianbergmann/phpunit/issues#issue/125 for details +// Use dirname(__DIR__) instead of using /../ because of https://github.com/facebook/hhvm/issues/5215 +require_once dirname(__DIR__) . '/Framework/Error.php'; +require_once dirname(__DIR__) . '/Framework/Error/Notice.php'; +require_once dirname(__DIR__) . '/Framework/Error/Warning.php'; +require_once dirname(__DIR__) . '/Framework/Error/Deprecated.php'; + +/** + * Error handler that converts PHP errors and warnings to exceptions. + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Util_ErrorHandler +{ + protected static $errorStack = array(); + + /** + * Returns the error stack. + * + * @return array + */ + public static function getErrorStack() + { + return self::$errorStack; + } + + /** + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * + * @throws PHPUnit_Framework_Error + */ + public static function handleError($errno, $errstr, $errfile, $errline) + { + if (!($errno & error_reporting())) { + return false; + } + + self::$errorStack[] = array($errno, $errstr, $errfile, $errline); + + $trace = debug_backtrace(false); + array_shift($trace); + + foreach ($trace as $frame) { + if ($frame['function'] == '__toString') { + return false; + } + } + + if ($errno == E_NOTICE || $errno == E_USER_NOTICE || $errno == E_STRICT) { + if (PHPUnit_Framework_Error_Notice::$enabled !== true) { + return false; + } + + $exception = 'PHPUnit_Framework_Error_Notice'; + } elseif ($errno == E_WARNING || $errno == E_USER_WARNING) { + if (PHPUnit_Framework_Error_Warning::$enabled !== true) { + return false; + } + + $exception = 'PHPUnit_Framework_Error_Warning'; + } elseif ($errno == E_DEPRECATED || $errno == E_USER_DEPRECATED) { + if (PHPUnit_Framework_Error_Deprecated::$enabled !== true) { + return false; + } + + $exception = 'PHPUnit_Framework_Error_Deprecated'; + } else { + $exception = 'PHPUnit_Framework_Error'; + } + + throw new $exception($errstr, $errno, $errfile, $errline); + } + + /** + * Registers an error handler and returns a function that will restore + * the previous handler when invoked + * + * @param int $severity PHP predefined error constant + * + * @throws Exception if event of specified severity is emitted + */ + public static function handleErrorOnce($severity = E_WARNING) + { + $terminator = function () { + static $expired = false; + if (!$expired) { + $expired = true; + // cleans temporary error handler + return restore_error_handler(); + } + }; + + set_error_handler(function ($errno, $errstr) use ($severity) { + if ($errno === $severity) { + return; + } + + return false; + }); + + return $terminator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility class for blacklisting PHPUnit's own source code files. + * + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Util_Blacklist +{ + /** + * @var array + */ + public static $blacklistedClassNames = array( + 'File_Iterator' => 1, + 'PHP_CodeCoverage' => 1, + 'PHP_Invoker' => 1, + 'PHP_Timer' => 1, + 'PHP_Token' => 1, + 'PHPUnit_Framework_TestCase' => 2, + 'PHPUnit_Extensions_Database_TestCase' => 2, + 'PHPUnit_Framework_MockObject_Generator' => 2, + 'PHPUnit_Extensions_SeleniumTestCase' => 2, + 'Text_Template' => 1, + 'Symfony\Component\Yaml\Yaml' => 1, + 'SebastianBergmann\Diff\Diff' => 1, + 'SebastianBergmann\Environment\Runtime' => 1, + 'SebastianBergmann\Comparator\Comparator' => 1, + 'SebastianBergmann\Exporter\Exporter' => 1, + 'SebastianBergmann\GlobalState\Snapshot' => 1, + 'SebastianBergmann\RecursionContext\Context' => 1, + 'SebastianBergmann\Version' => 1, + 'Composer\Autoload\ClassLoader' => 1, + 'Doctrine\Instantiator\Instantiator' => 1, + 'phpDocumentor\Reflection\DocBlock' => 1, + 'Prophecy\Prophet' => 1 + ); + + /** + * @var array + */ + private static $directories; + + /** + * @return array + * + * @since Method available since Release 4.1.0 + */ + public function getBlacklistedDirectories() + { + $this->initialize(); + + return self::$directories; + } + + /** + * @param string $file + * + * @return bool + */ + public function isBlacklisted($file) + { + if (defined('PHPUNIT_TESTSUITE')) { + return false; + } + + $this->initialize(); + + foreach (self::$directories as $directory) { + if (strpos($file, $directory) === 0) { + return true; + } + } + + return false; + } + + private function initialize() + { + if (self::$directories === null) { + self::$directories = array(); + + foreach (self::$blacklistedClassNames as $className => $parent) { + if (!class_exists($className)) { + continue; + } + + $reflector = new ReflectionClass($className); + $directory = $reflector->getFileName(); + + for ($i = 0; $i < $parent; $i++) { + $directory = dirname($directory); + } + + self::$directories[] = $directory; + } + + // Hide process isolation workaround on Windows. + // @see PHPUnit_Util_PHP::factory() + // @see PHPUnit_Util_PHP_Windows::process() + if (DIRECTORY_SEPARATOR === '\\') { + // tempnam() prefix is limited to first 3 chars. + // @see http://php.net/manual/en/function.tempnam.php + self::$directories[] = sys_get_temp_dir() . '\\PHP'; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Prints TestDox documentation in text format. + * + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_TestDox_ResultPrinter_Text extends PHPUnit_Util_TestDox_ResultPrinter +{ + /** + * Handler for 'start class' event. + * + * @param string $name + */ + protected function startClass($name) + { + $this->write($this->currentTestClassPrettified . "\n"); + } + + /** + * Handler for 'on test' event. + * + * @param string $name + * @param bool $success + */ + protected function onTest($name, $success = true) + { + if ($success) { + $this->write(' [x] '); + } else { + $this->write(' [ ] '); + } + + $this->write($name . "\n"); + } + + /** + * Handler for 'end class' event. + * + * @param string $name + */ + protected function endClass($name) + { + $this->write("\n"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Prints TestDox documentation in HTML format. + * + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_TestDox_ResultPrinter_HTML extends PHPUnit_Util_TestDox_ResultPrinter +{ + /** + * @var bool + */ + protected $printsHTML = true; + + /** + * Handler for 'start run' event. + */ + protected function startRun() + { + $this->write(''); + } + + /** + * Handler for 'start class' event. + * + * @param string $name + */ + protected function startClass($name) + { + $this->write( + '

' . $this->currentTestClassPrettified . + '

    ' + ); + } + + /** + * Handler for 'on test' event. + * + * @param string $name + * @param bool $success + */ + protected function onTest($name, $success = true) + { + if (!$success) { + $strikeOpen = ''; + $strikeClose = ''; + } else { + $strikeOpen = ''; + $strikeClose = ''; + } + + $this->write('
  • ' . $strikeOpen . $name . $strikeClose . '
  • '); + } + + /** + * Handler for 'end class' event. + * + * @param string $name + */ + protected function endClass($name) + { + $this->write('
'); + } + + /** + * Handler for 'end run' event. + */ + protected function endRun() + { + $this->write(''); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for printers of TestDox documentation. + * + * @since Class available since Release 2.1.0 + */ +abstract class PHPUnit_Util_TestDox_ResultPrinter extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener +{ + /** + * @var PHPUnit_Util_TestDox_NamePrettifier + */ + protected $prettifier; + + /** + * @var string + */ + protected $testClass = ''; + + /** + * @var int + */ + protected $testStatus = false; + + /** + * @var array + */ + protected $tests = array(); + + /** + * @var int + */ + protected $successful = 0; + + /** + * @var int + */ + protected $failed = 0; + + /** + * @var int + */ + protected $risky = 0; + + /** + * @var int + */ + protected $skipped = 0; + + /** + * @var int + */ + protected $incomplete = 0; + + /** + * @var string + */ + protected $currentTestClassPrettified; + + /** + * @var string + */ + protected $currentTestMethodPrettified; + + /** + * Constructor. + * + * @param resource $out + */ + public function __construct($out = null) + { + parent::__construct($out); + + $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier; + $this->startRun(); + } + + /** + * Flush buffer and close output. + */ + public function flush() + { + $this->doEndClass(); + $this->endRun(); + + parent::flush(); + } + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; + $this->failed++; + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; + $this->failed++; + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE; + $this->incomplete++; + } + + /** + * Risky test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 4.0.0 + */ + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY; + $this->risky++; + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED; + $this->skipped++; + } + + /** + * A testsuite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A testsuite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + if (!$this->isOfInterest($test)) { + return; + } + + $class = get_class($test); + + if ($this->testClass != $class) { + if ($this->testClass != '') { + $this->doEndClass(); + } + + $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class); + $this->startClass($class); + + $this->testClass = $class; + $this->tests = array(); + } + + $prettified = false; + + $annotations = $test->getAnnotations(); + + if (isset($annotations['method']['testdox'][0])) { + $this->currentTestMethodPrettified = $annotations['method']['testdox'][0]; + $prettified = true; + } + + if (!$prettified) { + $this->currentTestMethodPrettified = $this->prettifier->prettifyTestMethod($test->getName(false)); + } + + $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED; + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if (!$this->isOfInterest($test)) { + return; + } + + if (!isset($this->tests[$this->currentTestMethodPrettified])) { + if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + $this->tests[$this->currentTestMethodPrettified]['success'] = 1; + $this->tests[$this->currentTestMethodPrettified]['failure'] = 0; + } else { + $this->tests[$this->currentTestMethodPrettified]['success'] = 0; + $this->tests[$this->currentTestMethodPrettified]['failure'] = 1; + } + } else { + if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + $this->tests[$this->currentTestMethodPrettified]['success']++; + } else { + $this->tests[$this->currentTestMethodPrettified]['failure']++; + } + } + + $this->currentTestClassPrettified = null; + $this->currentTestMethodPrettified = null; + } + + /** + * @since Method available since Release 2.3.0 + */ + protected function doEndClass() + { + foreach ($this->tests as $name => $data) { + $this->onTest($name, $data['failure'] == 0); + } + + $this->endClass($this->testClass); + } + + /** + * Handler for 'start run' event. + */ + protected function startRun() + { + } + + /** + * Handler for 'start class' event. + * + * @param string $name + */ + protected function startClass($name) + { + } + + /** + * Handler for 'on test' event. + * + * @param string $name + * @param bool $success + */ + protected function onTest($name, $success = true) + { + } + + /** + * Handler for 'end class' event. + * + * @param string $name + */ + protected function endClass($name) + { + } + + /** + * Handler for 'end run' event. + */ + protected function endRun() + { + } + + private function isOfInterest(PHPUnit_Framework_Test $test) + { + return $test instanceof PHPUnit_Framework_TestCase && get_class($test) != 'PHPUnit_Framework_Warning'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Prettifies class and method names for use in TestDox documentation. + * + * @since Class available since Release 2.1.0 + */ +class PHPUnit_Util_TestDox_NamePrettifier +{ + /** + * @var string + */ + protected $prefix = 'Test'; + + /** + * @var string + */ + protected $suffix = 'Test'; + + /** + * @var array + */ + protected $strings = array(); + + /** + * Prettifies the name of a test class. + * + * @param string $name + * + * @return string + */ + public function prettifyTestClass($name) + { + $title = $name; + + if ($this->suffix !== null && + $this->suffix == substr($name, -1 * strlen($this->suffix))) { + $title = substr($title, 0, strripos($title, $this->suffix)); + } + + if ($this->prefix !== null && + $this->prefix == substr($name, 0, strlen($this->prefix))) { + $title = substr($title, strlen($this->prefix)); + } + + if (substr($title, 0, 1) == '\\') { + $title = substr($title, 1); + } + + return $title; + } + + /** + * Prettifies the name of a test method. + * + * @param string $name + * + * @return string + */ + public function prettifyTestMethod($name) + { + $buffer = ''; + + if (!is_string($name) || strlen($name) == 0) { + return $buffer; + } + + $string = preg_replace('#\d+$#', '', $name, -1, $count); + + if (in_array($string, $this->strings)) { + $name = $string; + } elseif ($count == 0) { + $this->strings[] = $string; + } + + if (substr($name, 0, 4) == 'test') { + $name = substr($name, 4); + } + + $name[0] = strtoupper($name[0]); + + if (strpos($name, '_') !== false) { + return trim(str_replace('_', ' ', $name)); + } + + $max = strlen($name); + $wasNumeric = false; + + for ($i = 0; $i < $max; $i++) { + if ($i > 0 && + ord($name[$i]) >= 65 && + ord($name[$i]) <= 90) { + $buffer .= ' ' . strtolower($name[$i]); + } else { + $isNumeric = is_numeric($name[$i]); + + if (!$wasNumeric && $isNumeric) { + $buffer .= ' '; + $wasNumeric = true; + } + + if ($wasNumeric && !$isNumeric) { + $wasNumeric = false; + } + + $buffer .= $name[$i]; + } + } + + return $buffer; + } + + /** + * Sets the prefix of test names. + * + * @param string $prefix + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Sets the suffix of test names. + * + * @param string $suffix + */ + public function setSuffix($suffix) + { + $this->suffix = $suffix; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * XML helpers. + * + * @since Class available since Release 3.2.0 + */ +class PHPUnit_Util_XML +{ + /** + * Escapes a string for the use in XML documents + * Any Unicode character is allowed, excluding the surrogate blocks, FFFE, + * and FFFF (not even as character reference). + * See http://www.w3.org/TR/xml/#charsets + * + * @param string $string + * + * @return string + * + * @since Method available since Release 3.4.6 + */ + public static function prepareString($string) + { + return preg_replace( + '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', + '', + htmlspecialchars( + PHPUnit_Util_String::convertToUtf8($string), + ENT_QUOTES, + 'UTF-8' + ) + ); + } + + /** + * Loads an XML (or HTML) file into a DOMDocument object. + * + * @param string $filename + * @param bool $isHtml + * @param bool $xinclude + * @param bool $strict + * + * @return DOMDocument + * + * @since Method available since Release 3.3.0 + */ + public static function loadFile($filename, $isHtml = false, $xinclude = false, $strict = false) + { + $reporting = error_reporting(0); + $contents = file_get_contents($filename); + error_reporting($reporting); + + if ($contents === false) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Could not read "%s".', + $filename + ) + ); + } + + return self::load($contents, $isHtml, $filename, $xinclude, $strict); + } + + /** + * Load an $actual document into a DOMDocument. This is called + * from the selector assertions. + * + * If $actual is already a DOMDocument, it is returned with + * no changes. Otherwise, $actual is loaded into a new DOMDocument + * as either HTML or XML, depending on the value of $isHtml. If $isHtml is + * false and $xinclude is true, xinclude is performed on the loaded + * DOMDocument. + * + * Note: prior to PHPUnit 3.3.0, this method loaded a file and + * not a string as it currently does. To load a file into a + * DOMDocument, use loadFile() instead. + * + * @param string|DOMDocument $actual + * @param bool $isHtml + * @param string $filename + * @param bool $xinclude + * @param bool $strict + * + * @return DOMDocument + * + * @since Method available since Release 3.3.0 + */ + public static function load($actual, $isHtml = false, $filename = '', $xinclude = false, $strict = false) + { + if ($actual instanceof DOMDocument) { + return $actual; + } + + if (!is_string($actual)) { + throw new PHPUnit_Framework_Exception('Could not load XML from ' . gettype($actual)); + } + + if ($actual === '') { + throw new PHPUnit_Framework_Exception('Could not load XML from empty string'); + } + + // Required for XInclude on Windows. + if ($xinclude) { + $cwd = getcwd(); + @chdir(dirname($filename)); + } + + $document = new DOMDocument; + $document->preserveWhiteSpace = false; + + $internal = libxml_use_internal_errors(true); + $message = ''; + $reporting = error_reporting(0); + + if ('' !== $filename) { + // Necessary for xinclude + $document->documentURI = $filename; + } + + if ($isHtml) { + $loaded = $document->loadHTML($actual); + } else { + $loaded = $document->loadXML($actual); + } + + if (!$isHtml && $xinclude) { + $document->xinclude(); + } + + foreach (libxml_get_errors() as $error) { + $message .= "\n" . $error->message; + } + + libxml_use_internal_errors($internal); + error_reporting($reporting); + + if ($xinclude) { + @chdir($cwd); + } + + if ($loaded === false || ($strict && $message !== '')) { + if ($filename !== '') { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Could not load "%s".%s', + $filename, + $message != '' ? "\n" . $message : '' + ) + ); + } else { + if ($message === '') { + $message = 'Could not load XML for unknown reason'; + } + throw new PHPUnit_Framework_Exception($message); + } + } + + return $document; + } + + /** + * @param DOMNode $node + * + * @return string + * + * @since Method available since Release 3.4.0 + */ + public static function nodeToText(DOMNode $node) + { + if ($node->childNodes->length == 1) { + return $node->textContent; + } + + $result = ''; + + foreach ($node->childNodes as $childNode) { + $result .= $node->ownerDocument->saveXML($childNode); + } + + return $result; + } + + /** + * @param DOMNode $node + * + * @since Method available since Release 3.3.0 + */ + public static function removeCharacterDataNodes(DOMNode $node) + { + if ($node->hasChildNodes()) { + for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { + if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { + $node->removeChild($child); + } + } + } + } + + /** + * "Convert" a DOMElement object into a PHP variable. + * + * @param DOMElement $element + * + * @return mixed + * + * @since Method available since Release 3.4.0 + */ + public static function xmlToVariable(DOMElement $element) + { + $variable = null; + + switch ($element->tagName) { + case 'array': + $variable = array(); + + foreach ($element->getElementsByTagName('element') as $element) { + $item = $element->childNodes->item(0); + + if ($item instanceof DOMText) { + $item = $element->childNodes->item(1); + } + + $value = self::xmlToVariable($item); + + if ($element->hasAttribute('key')) { + $variable[(string) $element->getAttribute('key')] = $value; + } else { + $variable[] = $value; + } + } + break; + + case 'object': + $className = $element->getAttribute('class'); + + if ($element->hasChildNodes()) { + $arguments = $element->childNodes->item(1)->childNodes; + $constructorArgs = array(); + + foreach ($arguments as $argument) { + if ($argument instanceof DOMElement) { + $constructorArgs[] = self::xmlToVariable($argument); + } + } + + $class = new ReflectionClass($className); + $variable = $class->newInstanceArgs($constructorArgs); + } else { + $variable = new $className; + } + break; + + case 'boolean': + $variable = $element->textContent == 'true' ? true : false; + break; + + case 'integer': + case 'double': + case 'string': + $variable = $element->textContent; + + settype($variable, $element->tagName); + break; + } + + return $variable; + } + + /** + * Validate list of keys in the associative array. + * + * @param array $hash + * @param array $validKeys + * + * @return array + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.3.0 + */ + public static function assertValidKeys(array $hash, array $validKeys) + { + $valids = array(); + + // Normalize validation keys so that we can use both indexed and + // associative arrays. + foreach ($validKeys as $key => $val) { + is_int($key) ? $valids[$val] = null : $valids[$key] = $val; + } + + $validKeys = array_keys($valids); + + // Check for invalid keys. + foreach ($hash as $key => $value) { + if (!in_array($key, $validKeys)) { + $unknown[] = $key; + } + } + + if (!empty($unknown)) { + throw new PHPUnit_Framework_Exception( + 'Unknown key(s): ' . implode(', ', $unknown) + ); + } + + // Add default values for any valid keys that are empty. + foreach ($valids as $key => $value) { + if (!isset($hash[$key])) { + $hash[$key] = $value; + } + } + + return $hash; + } + + /** + * Parse a CSS selector into an associative array suitable for + * use with findNodes(). + * + * @param string $selector + * @param mixed $content + * + * @return array + * + * @since Method available since Release 3.3.0 + */ + public static function convertSelectToTag($selector, $content = true) + { + $selector = trim(preg_replace("/\s+/", ' ', $selector)); + + // substitute spaces within attribute value + while (preg_match('/\[[^\]]+"[^"]+\s[^"]+"\]/', $selector)) { + $selector = preg_replace( + '/(\[[^\]]+"[^"]+)\s([^"]+"\])/', + '$1__SPACE__$2', + $selector + ); + } + + if (strstr($selector, ' ')) { + $elements = explode(' ', $selector); + } else { + $elements = array($selector); + } + + $previousTag = array(); + + foreach (array_reverse($elements) as $element) { + $element = str_replace('__SPACE__', ' ', $element); + + // child selector + if ($element == '>') { + $previousTag = array('child' => $previousTag['descendant']); + continue; + } + + // adjacent-sibling selector + if ($element == '+') { + $previousTag = array('adjacent-sibling' => $previousTag['descendant']); + continue; + } + + $tag = array(); + + // match element tag + preg_match("/^([^\.#\[]*)/", $element, $eltMatches); + + if (!empty($eltMatches[1])) { + $tag['tag'] = $eltMatches[1]; + } + + // match attributes (\[[^\]]*\]*), ids (#[^\.#\[]*), + // and classes (\.[^\.#\[]*)) + preg_match_all( + "/(\[[^\]]*\]*|#[^\.#\[]*|\.[^\.#\[]*)/", + $element, + $matches + ); + + if (!empty($matches[1])) { + $classes = array(); + $attrs = array(); + + foreach ($matches[1] as $match) { + // id matched + if (substr($match, 0, 1) == '#') { + $tag['id'] = substr($match, 1); + } // class matched + elseif (substr($match, 0, 1) == '.') { + $classes[] = substr($match, 1); + } // attribute matched + elseif (substr($match, 0, 1) == '[' && + substr($match, -1, 1) == ']') { + $attribute = substr($match, 1, strlen($match) - 2); + $attribute = str_replace('"', '', $attribute); + + // match single word + if (strstr($attribute, '~=')) { + list($key, $value) = explode('~=', $attribute); + $value = "regexp:/.*\b$value\b.*/"; + } // match substring + elseif (strstr($attribute, '*=')) { + list($key, $value) = explode('*=', $attribute); + $value = "regexp:/.*$value.*/"; + } // exact match + else { + list($key, $value) = explode('=', $attribute); + } + + $attrs[$key] = $value; + } + } + + if (!empty($classes)) { + $tag['class'] = implode(' ', $classes); + } + + if (!empty($attrs)) { + $tag['attributes'] = $attrs; + } + } + + // tag content + if (is_string($content)) { + $tag['content'] = $content; + } + + // determine previous child/descendants + if (!empty($previousTag['descendant'])) { + $tag['descendant'] = $previousTag['descendant']; + } elseif (!empty($previousTag['child'])) { + $tag['child'] = $previousTag['child']; + } elseif (!empty($previousTag['adjacent-sibling'])) { + $tag['adjacent-sibling'] = $previousTag['adjacent-sibling']; + unset($tag['content']); + } + + $previousTag = array('descendant' => $tag); + } + + return $tag; + } + + /** + * Parse an $actual document and return an array of DOMNodes + * matching the CSS $selector. If an error occurs, it will + * return false. + * + * To only return nodes containing a certain content, give + * the $content to match as a string. Otherwise, setting + * $content to true will return all nodes matching $selector. + * + * The $actual document may be a DOMDocument or a string + * containing XML or HTML, identified by $isHtml. + * + * @param array $selector + * @param string $content + * @param mixed $actual + * @param bool $isHtml + * + * @return bool|array + * + * @since Method available since Release 3.3.0 + */ + public static function cssSelect($selector, $content, $actual, $isHtml = true) + { + $matcher = self::convertSelectToTag($selector, $content); + $dom = self::load($actual, $isHtml); + $tags = self::findNodes($dom, $matcher, $isHtml); + + return $tags; + } + + /** + * Parse out the options from the tag using DOM object tree. + * + * @param DOMDocument $dom + * @param array $options + * @param bool $isHtml + * + * @return array + * + * @since Method available since Release 3.3.0 + */ + public static function findNodes(DOMDocument $dom, array $options, $isHtml = true) + { + $valid = array( + 'id', 'class', 'tag', 'content', 'attributes', 'parent', + 'child', 'ancestor', 'descendant', 'children', 'adjacent-sibling' + ); + + $filtered = array(); + $options = self::assertValidKeys($options, $valid); + + // find the element by id + if ($options['id']) { + $options['attributes']['id'] = $options['id']; + } + + if ($options['class']) { + $options['attributes']['class'] = $options['class']; + } + + $nodes = array(); + + // find the element by a tag type + if ($options['tag']) { + if ($isHtml) { + $elements = self::getElementsByCaseInsensitiveTagName( + $dom, + $options['tag'] + ); + } else { + $elements = $dom->getElementsByTagName($options['tag']); + } + + foreach ($elements as $element) { + $nodes[] = $element; + } + + if (empty($nodes)) { + return false; + } + } // no tag selected, get them all + else { + $tags = array( + 'a', 'abbr', 'acronym', 'address', 'area', 'b', 'base', 'bdo', + 'big', 'blockquote', 'body', 'br', 'button', 'caption', 'cite', + 'code', 'col', 'colgroup', 'dd', 'del', 'div', 'dfn', 'dl', + 'dt', 'em', 'fieldset', 'form', 'frame', 'frameset', 'h1', 'h2', + 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html', 'i', 'iframe', + 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', + 'map', 'meta', 'noframes', 'noscript', 'object', 'ol', 'optgroup', + 'option', 'p', 'param', 'pre', 'q', 'samp', 'script', 'select', + 'small', 'span', 'strong', 'style', 'sub', 'sup', 'table', + 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'title', + 'tr', 'tt', 'ul', 'var', + // HTML5 + 'article', 'aside', 'audio', 'bdi', 'canvas', 'command', + 'datalist', 'details', 'dialog', 'embed', 'figure', 'figcaption', + 'footer', 'header', 'hgroup', 'keygen', 'mark', 'meter', 'nav', + 'output', 'progress', 'ruby', 'rt', 'rp', 'track', 'section', + 'source', 'summary', 'time', 'video', 'wbr' + ); + + foreach ($tags as $tag) { + if ($isHtml) { + $elements = self::getElementsByCaseInsensitiveTagName( + $dom, + $tag + ); + } else { + $elements = $dom->getElementsByTagName($tag); + } + + foreach ($elements as $element) { + $nodes[] = $element; + } + } + + if (empty($nodes)) { + return false; + } + } + + // filter by attributes + if ($options['attributes']) { + foreach ($nodes as $node) { + $invalid = false; + + foreach ($options['attributes'] as $name => $value) { + // match by regexp if like "regexp:/foo/i" + if (preg_match('/^regexp\s*:\s*(.*)/i', $value, $matches)) { + if (!preg_match($matches[1], $node->getAttribute($name))) { + $invalid = true; + } + } // class can match only a part + elseif ($name == 'class') { + // split to individual classes + $findClasses = explode( + ' ', + preg_replace("/\s+/", ' ', $value) + ); + + $allClasses = explode( + ' ', + preg_replace("/\s+/", ' ', $node->getAttribute($name)) + ); + + // make sure each class given is in the actual node + foreach ($findClasses as $findClass) { + if (!in_array($findClass, $allClasses)) { + $invalid = true; + } + } + } // match by exact string + else { + if ($node->getAttribute($name) != $value) { + $invalid = true; + } + } + } + + // if every attribute given matched + if (!$invalid) { + $filtered[] = $node; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return false; + } + } + + // filter by content + if ($options['content'] !== null) { + foreach ($nodes as $node) { + $invalid = false; + + // match by regexp if like "regexp:/foo/i" + if (preg_match('/^regexp\s*:\s*(.*)/i', $options['content'], $matches)) { + if (!preg_match($matches[1], self::getNodeText($node))) { + $invalid = true; + } + } // match empty string + elseif ($options['content'] === '') { + if (self::getNodeText($node) !== '') { + $invalid = true; + } + } // match by exact string + elseif (strstr(self::getNodeText($node), $options['content']) === false) { + $invalid = true; + } + + if (!$invalid) { + $filtered[] = $node; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return false; + } + } + + // filter by parent node + if ($options['parent']) { + $parentNodes = self::findNodes($dom, $options['parent'], $isHtml); + $parentNode = isset($parentNodes[0]) ? $parentNodes[0] : null; + + foreach ($nodes as $node) { + if ($parentNode !== $node->parentNode) { + continue; + } + + $filtered[] = $node; + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return false; + } + } + + // filter by child node + if ($options['child']) { + $childNodes = self::findNodes($dom, $options['child'], $isHtml); + $childNodes = !empty($childNodes) ? $childNodes : array(); + + foreach ($nodes as $node) { + foreach ($node->childNodes as $child) { + foreach ($childNodes as $childNode) { + if ($childNode === $child) { + $filtered[] = $node; + } + } + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return false; + } + } + + // filter by adjacent-sibling + if ($options['adjacent-sibling']) { + $adjacentSiblingNodes = self::findNodes($dom, $options['adjacent-sibling'], $isHtml); + $adjacentSiblingNodes = !empty($adjacentSiblingNodes) ? $adjacentSiblingNodes : array(); + + foreach ($nodes as $node) { + $sibling = $node; + + while ($sibling = $sibling->nextSibling) { + if ($sibling->nodeType !== XML_ELEMENT_NODE) { + continue; + } + + foreach ($adjacentSiblingNodes as $adjacentSiblingNode) { + if ($sibling === $adjacentSiblingNode) { + $filtered[] = $node; + break; + } + } + + break; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return false; + } + } + + // filter by ancestor + if ($options['ancestor']) { + $ancestorNodes = self::findNodes($dom, $options['ancestor'], $isHtml); + $ancestorNode = isset($ancestorNodes[0]) ? $ancestorNodes[0] : null; + + foreach ($nodes as $node) { + $parent = $node->parentNode; + + while ($parent && $parent->nodeType != XML_HTML_DOCUMENT_NODE) { + if ($parent === $ancestorNode) { + $filtered[] = $node; + } + + $parent = $parent->parentNode; + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return false; + } + } + + // filter by descendant + if ($options['descendant']) { + $descendantNodes = self::findNodes($dom, $options['descendant'], $isHtml); + $descendantNodes = !empty($descendantNodes) ? $descendantNodes : array(); + + foreach ($nodes as $node) { + foreach (self::getDescendants($node) as $descendant) { + foreach ($descendantNodes as $descendantNode) { + if ($descendantNode === $descendant) { + $filtered[] = $node; + } + } + } + } + + $nodes = $filtered; + $filtered = array(); + + if (empty($nodes)) { + return false; + } + } + + // filter by children + if ($options['children']) { + $validChild = array('count', 'greater_than', 'less_than', 'only'); + $childOptions = self::assertValidKeys( + $options['children'], + $validChild + ); + + foreach ($nodes as $node) { + $childNodes = $node->childNodes; + + foreach ($childNodes as $childNode) { + if ($childNode->nodeType !== XML_CDATA_SECTION_NODE && + $childNode->nodeType !== XML_TEXT_NODE) { + $children[] = $childNode; + } + } + + // we must have children to pass this filter + if (!empty($children)) { + // exact count of children + if ($childOptions['count'] !== null) { + if (count($children) !== $childOptions['count']) { + break; + } + } // range count of children + elseif ($childOptions['less_than'] !== null && + $childOptions['greater_than'] !== null) { + if (count($children) >= $childOptions['less_than'] || + count($children) <= $childOptions['greater_than']) { + break; + } + } // less than a given count + elseif ($childOptions['less_than'] !== null) { + if (count($children) >= $childOptions['less_than']) { + break; + } + } // more than a given count + elseif ($childOptions['greater_than'] !== null) { + if (count($children) <= $childOptions['greater_than']) { + break; + } + } + + // match each child against a specific tag + if ($childOptions['only']) { + $onlyNodes = self::findNodes( + $dom, + $childOptions['only'], + $isHtml + ); + + // try to match each child to one of the 'only' nodes + foreach ($children as $child) { + $matched = false; + + foreach ($onlyNodes as $onlyNode) { + if ($onlyNode === $child) { + $matched = true; + } + } + + if (!$matched) { + break 2; + } + } + } + + $filtered[] = $node; + } + } + + $nodes = $filtered; + + if (empty($nodes)) { + return; + } + } + + // return the first node that matches all criteria + return !empty($nodes) ? $nodes : array(); + } + + /** + * Recursively get flat array of all descendants of this node. + * + * @param DOMNode $node + * + * @return array + * + * @since Method available since Release 3.3.0 + */ + protected static function getDescendants(DOMNode $node) + { + $allChildren = array(); + $childNodes = $node->childNodes ? $node->childNodes : array(); + + foreach ($childNodes as $child) { + if ($child->nodeType === XML_CDATA_SECTION_NODE || + $child->nodeType === XML_TEXT_NODE) { + continue; + } + + $children = self::getDescendants($child); + $allChildren = array_merge($allChildren, $children, array($child)); + } + + return isset($allChildren) ? $allChildren : array(); + } + + /** + * Gets elements by case insensitive tagname. + * + * @param DOMDocument $dom + * @param string $tag + * + * @return DOMNodeList + * + * @since Method available since Release 3.4.0 + */ + protected static function getElementsByCaseInsensitiveTagName(DOMDocument $dom, $tag) + { + $elements = $dom->getElementsByTagName(strtolower($tag)); + + if ($elements->length == 0) { + $elements = $dom->getElementsByTagName(strtoupper($tag)); + } + + return $elements; + } + + /** + * Get the text value of this node's child text node. + * + * @param DOMNode $node + * + * @return string + * + * @since Method available since Release 3.3.0 + */ + protected static function getNodeText(DOMNode $node) + { + if (!$node->childNodes instanceof DOMNodeList) { + return ''; + } + + $result = ''; + + foreach ($node->childNodes as $childNode) { + if ($childNode->nodeType === XML_TEXT_NODE || + $childNode->nodeType === XML_CDATA_SECTION_NODE) { + $result .= trim($childNode->data) . ' '; + } else { + $result .= self::getNodeText($childNode); + } + } + + return str_replace(' ', ' ', $result); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility class that can print to STDOUT or write to a file. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Util_Printer +{ + /** + * If true, flush output after every write. + * + * @var bool + */ + protected $autoFlush = false; + + /** + * @var resource + */ + protected $out; + + /** + * @var string + */ + protected $outTarget; + + /** + * @var bool + */ + protected $printsHTML = false; + + /** + * Constructor. + * + * @param mixed $out + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($out = null) + { + if ($out !== null) { + if (is_string($out)) { + if (strpos($out, 'socket://') === 0) { + $out = explode(':', str_replace('socket://', '', $out)); + + if (sizeof($out) != 2) { + throw new PHPUnit_Framework_Exception; + } + + $this->out = fsockopen($out[0], $out[1]); + } else { + if (strpos($out, 'php://') === false && + !is_dir(dirname($out))) { + mkdir(dirname($out), 0777, true); + } + + $this->out = fopen($out, 'wt'); + } + + $this->outTarget = $out; + } else { + $this->out = $out; + } + } + } + + /** + * Flush buffer, optionally tidy up HTML, and close output if it's not to a php stream + */ + public function flush() + { + if ($this->out && strncmp($this->outTarget, 'php://', 6) !== 0) { + fclose($this->out); + } + + if ($this->printsHTML === true && + $this->outTarget !== null && + strpos($this->outTarget, 'php://') !== 0 && + strpos($this->outTarget, 'socket://') !== 0 && + extension_loaded('tidy')) { + file_put_contents( + $this->outTarget, + tidy_repair_file( + $this->outTarget, + array('indent' => true, 'wrap' => 0), + 'utf8' + ) + ); + } + } + + /** + * Performs a safe, incremental flush. + * + * Do not confuse this function with the flush() function of this class, + * since the flush() function may close the file being written to, rendering + * the current object no longer usable. + * + * @since Method available since Release 3.3.0 + */ + public function incrementalFlush() + { + if ($this->out) { + fflush($this->out); + } else { + flush(); + } + } + + /** + * @param string $buffer + */ + public function write($buffer) + { + if ($this->out) { + fwrite($this->out, $buffer); + + if ($this->autoFlush) { + $this->incrementalFlush(); + } + } else { + if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') { + $buffer = htmlspecialchars($buffer); + } + + print $buffer; + + if ($this->autoFlush) { + $this->incrementalFlush(); + } + } + } + + /** + * Check auto-flush mode. + * + * @return bool + * + * @since Method available since Release 3.3.0 + */ + public function getAutoFlush() + { + return $this->autoFlush; + } + + /** + * Set auto-flushing mode. + * + * If set, *incremental* flushes will be done after each write. This should + * not be confused with the different effects of this class' flush() method. + * + * @param bool $autoFlush + * + * @since Method available since Release 3.3.0 + */ + public function setAutoFlush($autoFlush) + { + if (is_bool($autoFlush)) { + $this->autoFlush = $autoFlush; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Decorator for Tests. + * + * Use TestDecorator as the base class for defining new + * test decorators. Test decorator subclasses can be introduced + * to add behaviour before or after a test is run. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Extensions_TestDecorator extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing +{ + /** + * The Test to be decorated. + * + * @var object + */ + protected $test = null; + + /** + * Constructor. + * + * @param PHPUnit_Framework_Test $test + */ + public function __construct(PHPUnit_Framework_Test $test) + { + $this->test = $test; + } + + /** + * Returns a string representation of the test. + * + * @return string + */ + public function toString() + { + return $this->test->toString(); + } + + /** + * Runs the test and collects the + * result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + */ + public function basicRun(PHPUnit_Framework_TestResult $result) + { + $this->test->run($result); + } + + /** + * Counts the number of test cases that + * will be run by this test. + * + * @return int + */ + public function count() + { + return count($this->test); + } + + /** + * Creates a default TestResult object. + * + * @return PHPUnit_Framework_TestResult + */ + protected function createResult() + { + return new PHPUnit_Framework_TestResult; + } + + /** + * Returns the test to be run. + * + * @return PHPUnit_Framework_Test + */ + public function getTest() + { + return $this->test; + } + + /** + * Runs the decorated test and collects the + * result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + * + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = null) + { + if ($result === null) { + $result = $this->createResult(); + } + + $this->basicRun($result); + + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Runner for PHPT test cases. + * + * @since Class available since Release 3.1.4 + */ +class PHPUnit_Extensions_PhptTestCase implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing +{ + /** + * @var string + */ + private $filename; + + /** + * @var array + */ + private $settings = array( + 'allow_url_fopen=1', + 'auto_append_file=', + 'auto_prepend_file=', + 'disable_functions=', + 'display_errors=1', + 'docref_root=', + 'docref_ext=.html', + 'error_append_string=', + 'error_prepend_string=', + 'error_reporting=-1', + 'html_errors=0', + 'log_errors=0', + 'magic_quotes_runtime=0', + 'output_handler=', + 'open_basedir=', + 'output_buffering=Off', + 'report_memleaks=0', + 'report_zend_debug=0', + 'safe_mode=0', + 'track_errors=1', + 'xdebug.default_enable=0' + ); + + /** + * Constructs a test case with the given filename. + * + * @param string $filename + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($filename) + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_file($filename)) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'File "%s" does not exist.', + $filename + ) + ); + } + + $this->filename = $filename; + } + + /** + * Counts the number of test cases executed by run(TestResult result). + * + * @return int + */ + public function count() + { + return 1; + } + + /** + * Runs a test and collects its result in a TestResult instance. + * + * @param PHPUnit_Framework_TestResult $result + * + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = null) + { + $sections = $this->parse(); + $code = $this->render($sections['FILE']); + + if ($result === null) { + $result = new PHPUnit_Framework_TestResult; + } + + $php = PHPUnit_Util_PHP::factory(); + $skip = false; + $time = 0; + $settings = $this->settings; + + $result->startTest($this); + + if (isset($sections['INI'])) { + $settings = array_merge($settings, $this->parseIniSection($sections['INI'])); + } + + if (isset($sections['SKIPIF'])) { + $jobResult = $php->runJob($sections['SKIPIF'], $settings); + + if (!strncasecmp('skip', ltrim($jobResult['stdout']), 4)) { + if (preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $message)) { + $message = substr($message[1], 2); + } else { + $message = ''; + } + + $result->addFailure($this, new PHPUnit_Framework_SkippedTestError($message), 0); + + $skip = true; + } + } + + if (!$skip) { + PHP_Timer::start(); + $jobResult = $php->runJob($code, $settings); + $time = PHP_Timer::stop(); + + if (isset($sections['EXPECT'])) { + $assertion = 'assertEquals'; + $expected = $sections['EXPECT']; + } else { + $assertion = 'assertStringMatchesFormat'; + $expected = $sections['EXPECTF']; + } + + $output = preg_replace('/\r\n/', "\n", trim($jobResult['stdout'])); + $expected = preg_replace('/\r\n/', "\n", trim($expected)); + + try { + PHPUnit_Framework_Assert::$assertion($expected, $output); + } catch (PHPUnit_Framework_AssertionFailedError $e) { + $result->addFailure($this, $e, $time); + } catch (Throwable $t) { + $result->addError($this, $t, $time); + } catch (Exception $e) { + $result->addError($this, $e, $time); + } + } + + $result->endTest($this, $time); + + return $result; + } + + /** + * Returns the name of the test case. + * + * @return string + */ + public function getName() + { + return $this->toString(); + } + + /** + * Returns a string representation of the test case. + * + * @return string + */ + public function toString() + { + return $this->filename; + } + + /** + * @return array + * + * @throws PHPUnit_Framework_Exception + */ + private function parse() + { + $sections = array(); + $section = ''; + + foreach (file($this->filename) as $line) { + if (preg_match('/^--([_A-Z]+)--/', $line, $result)) { + $section = $result[1]; + $sections[$section] = ''; + continue; + } elseif (empty($section)) { + throw new PHPUnit_Framework_Exception('Invalid PHPT file'); + } + + $sections[$section] .= $line; + } + + if (!isset($sections['FILE']) || + (!isset($sections['EXPECT']) && !isset($sections['EXPECTF']))) { + throw new PHPUnit_Framework_Exception('Invalid PHPT file'); + } + + return $sections; + } + + /** + * @param string $code + * + * @return string + */ + private function render($code) + { + return str_replace( + array( + '__DIR__', + '__FILE__' + ), + array( + "'" . dirname($this->filename) . "'", + "'" . $this->filename . "'" + ), + $code + ); + } + + /** + * Parse --INI-- section key value pairs and return as array. + * + * @param string + * + * @return array + */ + protected function parseIniSection($content) + { + return preg_split('/\n|\r/', $content, -1, PREG_SPLIT_NO_EMPTY); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for test listeners that interact with an issue tracker. + * + * @since Class available since Release 3.4.0 + */ +abstract class PHPUnit_Extensions_TicketListener implements PHPUnit_Framework_TestListener +{ + /** + * @var array + */ + protected $ticketCounts = array(); + + /** + * @var bool + */ + protected $ran = false; + + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + } + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * Risky test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 4.0.0 + */ + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + /** + * A test suite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test suite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + if (!$test instanceof PHPUnit_Framework_Warning) { + if ($this->ran) { + return; + } + + $name = $test->getName(false); + $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name); + + foreach ($tickets as $ticket) { + $this->ticketCounts[$ticket][$name] = 1; + } + + $this->ran = true; + } + } + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + if (!$test instanceof PHPUnit_Framework_Warning) { + if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + $ifStatus = array('assigned', 'new', 'reopened'); + $newStatus = 'closed'; + $message = 'Automatically closed by PHPUnit (test passed).'; + $resolution = 'fixed'; + $cumulative = true; + } elseif ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) { + $ifStatus = array('closed'); + $newStatus = 'reopened'; + $message = 'Automatically reopened by PHPUnit (test failed).'; + $resolution = ''; + $cumulative = false; + } else { + return; + } + + $name = $test->getName(false); + $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name); + + foreach ($tickets as $ticket) { + // Remove this test from the totals (if it passed). + if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { + unset($this->ticketCounts[$ticket][$name]); + } + + // Only close tickets if ALL referenced cases pass + // but reopen tickets if a single test fails. + if ($cumulative) { + // Determine number of to-pass tests: + if (count($this->ticketCounts[$ticket]) > 0) { + // There exist remaining test cases with this reference. + $adjustTicket = false; + } else { + // No remaining tickets, go ahead and adjust. + $adjustTicket = true; + } + } else { + $adjustTicket = true; + } + + $ticketInfo = $this->getTicketInfo($ticket); + + if ($adjustTicket && in_array($ticketInfo['status'], $ifStatus)) { + $this->updateTicket($ticket, $newStatus, $message, $resolution); + } + } + } + } + + /** + * @param mixed $ticketId + * + * @return mixed + */ + abstract protected function getTicketInfo($ticketId = null); + + /** + * @param string $ticketId + * @param string $newStatus + * @param string $message + * @param string $resolution + */ + abstract protected function updateTicket($ticketId, $newStatus, $message, $resolution); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Suite for .phpt test cases. + * + * @since Class available since Release 3.1.4 + */ +class PHPUnit_Extensions_PhptTestSuite extends PHPUnit_Framework_TestSuite +{ + /** + * Constructs a new TestSuite for .phpt test cases. + * + * @param string $directory + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($directory) + { + if (is_string($directory) && is_dir($directory)) { + $this->setName($directory); + + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray($directory, '.phpt'); + + foreach ($files as $file) { + $this->addTestFile($file); + } + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'directory name'); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Decorator that runs a test repeatedly. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator +{ + /** + * @var bool + */ + protected $processIsolation = false; + + /** + * @var int + */ + protected $timesRepeat = 1; + + /** + * @param PHPUnit_Framework_Test $test + * @param int $timesRepeat + * @param bool $processIsolation + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $processIsolation = false) + { + parent::__construct($test); + + if (is_integer($timesRepeat) && + $timesRepeat >= 0) { + $this->timesRepeat = $timesRepeat; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'positive integer' + ); + } + + $this->processIsolation = $processIsolation; + } + + /** + * Counts the number of test cases that + * will be run by this test. + * + * @return int + */ + public function count() + { + return $this->timesRepeat * count($this->test); + } + + /** + * Runs the decorated test and collects the + * result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + * + * @return PHPUnit_Framework_TestResult + * + * @throws PHPUnit_Framework_Exception + */ + public function run(PHPUnit_Framework_TestResult $result = null) + { + if ($result === null) { + $result = $this->createResult(); + } + + //@codingStandardsIgnoreStart + for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) { + //@codingStandardsIgnoreEnd + if ($this->test instanceof PHPUnit_Framework_TestSuite) { + $this->test->setRunTestInSeparateProcess($this->processIsolation); + } + $this->test->run($result); + } + + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * We have a TestSuite object A. + * In TestSuite object A we have Tests tagged with @group. + * We want a TestSuite object B that contains TestSuite objects C, D, ... + * for the Tests tagged with @group C, @group D, ... + * Running the Tests from TestSuite object B results in Tests tagged with both + * + * @group C and @group D in TestSuite object A to be run twice . + * + * + * $suite = new PHPUnit_Extensions_GroupTestSuite($A, array('C', 'D')); + * + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Extensions_GroupTestSuite extends PHPUnit_Framework_TestSuite +{ + public function __construct(PHPUnit_Framework_TestSuite $suite, array $groups) + { + $groupSuites = array(); + $name = $suite->getName(); + + foreach ($groups as $group) { + $groupSuites[$group] = new PHPUnit_Framework_TestSuite($name . ' - ' . $group); + $this->addTest($groupSuites[$group]); + } + + $tests = new RecursiveIteratorIterator( + new PHPUnit_Util_TestSuiteIterator($suite), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($tests as $test) { + if ($test instanceof PHPUnit_Framework_TestCase) { + $testGroups = PHPUnit_Util_Test::getGroups( + get_class($test), + $test->getName(false) + ); + + foreach ($groups as $group) { + foreach ($testGroups as $testGroup) { + if ($group == $testGroup) { + $groupSuites[$group]->addTest($test); + } + } + } + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Listener for test progress. + * + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Framework_TestListener +{ + /** + * An error occurred. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time); + + /** + * A failure occurred. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time); + + /** + * Incomplete test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time); + + /** + * Risky test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 4.0.0 + */ + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time); + + /** + * Skipped test. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + * + * @since Method available since Release 3.0.0 + */ + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time); + + /** + * A test suite started. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite); + + /** + * A test suite ended. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite); + + /** + * A test started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test); + + /** + * A test ended. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wrapper for PHP deprecated errors. + * You can disable deprecated-to-exception conversion by setting + * + * + * PHPUnit_Framework_Error_Deprecated::$enabled = false; + * + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Error_Deprecated extends PHPUnit_Framework_Error +{ + public static $enabled = true; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wrapper for PHP warnings. + * You can disable notice-to-exception conversion by setting + * + * + * PHPUnit_Framework_Error_Warning::$enabled = false; + * + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Error_Warning extends PHPUnit_Framework_Error +{ + public static $enabled = true; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wrapper for PHP notices. + * You can disable notice-to-exception conversion by setting + * + * + * PHPUnit_Framework_Error_Notice::$enabled = false; + * + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Error_Notice extends PHPUnit_Framework_Error +{ + public static $enabled = true; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A warning. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_Warning extends PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + protected $message = ''; + + /** + * @var bool + */ + protected $backupGlobals = false; + + /** + * @var bool + */ + protected $backupStaticAttributes = false; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * @var bool + */ + protected $useErrorHandler = false; + + /** + * @param string $message + */ + public function __construct($message = '') + { + $this->message = $message; + parent::__construct('Warning'); + } + + /** + * @throws PHPUnit_Framework_Exception + */ + protected function runTest() + { + $this->fail($this->message); + } + + /** + * @return string + * + * @since Method available since Release 3.0.0 + */ + public function getMessage() + { + return $this->message; + } + + /** + * Returns a string representation of the test case. + * + * @return string + * + * @since Method available since Release 3.4.0 + */ + public function toString() + { + return 'Warning'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Framework_InvalidCoversTargetException extends PHPUnit_Framework_CodeCoverageException +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A Test can be run and collect its results. + * + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Framework_Test extends Countable +{ + /** + * Runs a test and collects its result in a TestResult instance. + * + * @param PHPUnit_Framework_TestResult $result + * + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = null); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_TestSuite_DataProvider extends PHPUnit_Framework_TestSuite +{ + /** + * Sets the dependencies of a TestCase. + * + * @param array $dependencies + */ + public function setDependencies(array $dependencies) + { + foreach ($this->tests as $test) { + $test->setDependencies($dependencies); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SebastianBergmann\Exporter\Exporter; + +/** + * Abstract base class for constraints. which are placed upon any value. + * + * @since Interface available since Release 3.0.0 + */ +abstract class PHPUnit_Framework_Constraint implements Countable, PHPUnit_Framework_SelfDescribing +{ + protected $exporter; + + public function __construct() + { + $this->exporter = new Exporter; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = false; + + if ($this->matches($other)) { + $success = true; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return false; + } + + /** + * Counts the number of constraint elements. + * + * @return int + * + * @since Method available since Release 3.4.0 + */ + public function count() + { + return 1; + } + + /** + * Throws an exception for the given compared value and test description + * + * @param mixed $other Evaluated value or object. + * @param string $description Additional information about the test + * @param SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + protected function fail($other, $description, SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null) + { + $failureDescription = sprintf( + 'Failed asserting that %s.', + $this->failureDescription($other) + ); + + $additionalFailureDescription = $this->additionalFailureDescription($other); + + if ($additionalFailureDescription) { + $failureDescription .= "\n" . $additionalFailureDescription; + } + + if (!empty($description)) { + $failureDescription = $description . "\n" . $failureDescription; + } + + throw new PHPUnit_Framework_ExpectationFailedException( + $failureDescription, + $comparisonFailure + ); + } + + /** + * Return additional failure description where needed + * + * The function can be overridden to provide additional failure + * information like a diff + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function additionalFailureDescription($other) + { + return ''; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * To provide additional failure information additionalFailureDescription + * can be used. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return $this->exporter->export($other) . ' ' . $this->toString(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Creates a synthetic failed assertion. + * + * @since Class available since Release 3.5.0 + */ +class PHPUnit_Framework_SyntheticError extends PHPUnit_Framework_AssertionFailedError +{ + /** + * The synthetic file. + * + * @var string + */ + protected $syntheticFile = ''; + + /** + * The synthetic line number. + * + * @var int + */ + protected $syntheticLine = 0; + + /** + * The synthetic trace. + * + * @var array + */ + protected $syntheticTrace = array(); + + /** + * Constructor. + * + * @param string $message + * @param int $code + * @param string $file + * @param int $line + * @param array $trace + */ + public function __construct($message, $code, $file, $line, $trace) + { + parent::__construct($message, $code); + + $this->syntheticFile = $file; + $this->syntheticLine = $line; + $this->syntheticTrace = $trace; + } + + /** + * @return string + */ + public function getSyntheticFile() + { + return $this->syntheticFile; + } + + /** + * @return int + */ + public function getSyntheticLine() + { + return $this->syntheticLine; + } + + /** + * @return array + */ + public function getSyntheticTrace() + { + return $this->syntheticTrace; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a test that printed output. + * + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_OutputError extends PHPUnit_Framework_AssertionFailedError +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A set of assert methods. + * + * @since Class available since Release 2.0.0 + */ +abstract class PHPUnit_Framework_Assert +{ + /** + * @var int + */ + private static $count = 0; + + /** + * Asserts that an array has a specified key. + * + * @param mixed $key + * @param array|ArrayAccess $array + * @param string $message + * + * @since Method available since Release 3.0.0 + */ + public static function assertArrayHasKey($key, $array, $message = '') + { + if (!(is_integer($key) || is_string($key))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'integer or string' + ); + } + + if (!(is_array($array) || $array instanceof ArrayAccess)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array or ArrayAccess' + ); + } + + $constraint = new PHPUnit_Framework_Constraint_ArrayHasKey($key); + + self::assertThat($array, $constraint, $message); + } + + /** + * Asserts that an array has a specified subset. + * + * @param array|ArrayAccess $subset + * @param array|ArrayAccess $array + * @param bool $strict Check for object identity + * @param string $message + * + * @since Method available since Release 4.4.0 + */ + public static function assertArraySubset($subset, $array, $strict = false, $message = '') + { + if (!is_array($subset)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'array or ArrayAccess' + ); + } + + if (!is_array($array)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array or ArrayAccess' + ); + } + + $constraint = new PHPUnit_Framework_Constraint_ArraySubset($subset, $strict); + + self::assertThat($array, $constraint, $message); + } + + /** + * Asserts that an array does not have a specified key. + * + * @param mixed $key + * @param array|ArrayAccess $array + * @param string $message + * + * @since Method available since Release 3.0.0 + */ + public static function assertArrayNotHasKey($key, $array, $message = '') + { + if (!(is_integer($key) || is_string($key))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'integer or string' + ); + } + + if (!(is_array($array) || $array instanceof ArrayAccess)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array or ArrayAccess' + ); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ArrayHasKey($key) + ); + + self::assertThat($array, $constraint, $message); + } + + /** + * Asserts that a haystack contains a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 2.1.0 + */ + public static function assertContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) + { + if (is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable) { + $constraint = new PHPUnit_Framework_Constraint_TraversableContains( + $needle, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ); + } elseif (is_string($haystack)) { + if (!is_string($needle)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'string' + ); + } + + $constraint = new PHPUnit_Framework_Constraint_StringContains( + $needle, + $ignoreCase + ); + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array, traversable or string' + ); + } + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 3.0.0 + */ + public static function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) + { + self::assertContains( + $needle, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message, + $ignoreCase, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ); + } + + /** + * Asserts that a haystack does not contain a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 2.1.0 + */ + public static function assertNotContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) + { + if (is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable) { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_TraversableContains( + $needle, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ) + ); + } elseif (is_string($haystack)) { + if (!is_string($needle)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'string' + ); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringContains( + $needle, + $ignoreCase + ) + ); + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array, traversable or string' + ); + } + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 3.0.0 + */ + public static function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) + { + self::assertNotContains( + $needle, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message, + $ignoreCase, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ); + } + + /** + * Asserts that a haystack contains only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ + public static function assertContainsOnly($type, $haystack, $isNativeType = null, $message = '') + { + if (!(is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array or traversable' + ); + } + + if ($isNativeType == null) { + $isNativeType = PHPUnit_Util_Type::isType($type); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_TraversableContainsOnly( + $type, + $isNativeType + ), + $message + ); + } + + /** + * Asserts that a haystack contains only instances of a given classname + * + * @param string $classname + * @param array|Traversable $haystack + * @param string $message + */ + public static function assertContainsOnlyInstancesOf($classname, $haystack, $message = '') + { + if (!(is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array or traversable' + ); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_TraversableContainsOnly( + $classname, + false + ), + $message + ); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains only values of a given type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ + public static function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '') + { + self::assertContainsOnly( + $type, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $isNativeType, + $message + ); + } + + /** + * Asserts that a haystack does not contain only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ + public static function assertNotContainsOnly($type, $haystack, $isNativeType = null, $message = '') + { + if (!(is_array($haystack) || + is_object($haystack) && $haystack instanceof Traversable)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 2, + 'array or traversable' + ); + } + + if ($isNativeType == null) { + $isNativeType = PHPUnit_Util_Type::isType($type); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_TraversableContainsOnly( + $type, + $isNativeType + ) + ), + $message + ); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain only values of a given + * type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ + public static function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '') + { + self::assertNotContainsOnly( + $type, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $isNativeType, + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param int $expectedCount + * @param mixed $haystack + * @param string $message + */ + public static function assertCount($expectedCount, $haystack, $message = '') + { + if (!is_int($expectedCount)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + if (!$haystack instanceof Countable && + !$haystack instanceof Traversable && + !is_array($haystack)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable'); + } + + self::assertThat( + $haystack, + new PHPUnit_Framework_Constraint_Count($expectedCount), + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param int $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.6.0 + */ + public static function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertCount( + $expectedCount, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param int $expectedCount + * @param mixed $haystack + * @param string $message + */ + public static function assertNotCount($expectedCount, $haystack, $message = '') + { + if (!is_int($expectedCount)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + if (!$haystack instanceof Countable && + !$haystack instanceof Traversable && + !is_array($haystack)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_Count($expectedCount) + ); + + self::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param int $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.6.0 + */ + public static function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertNotCount( + $expectedCount, + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that two variables are equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + */ + public static function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + $constraint = new PHPUnit_Framework_Constraint_IsEqual( + $expected, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a variable is equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + */ + public static function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + self::assertEquals( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that two variables are not equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 2.3.0 + */ + public static function assertNotEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsEqual( + $expected, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ) + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a variable is not equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + */ + public static function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + self::assertNotEquals( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that a variable is empty. + * + * @param mixed $actual + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertEmpty($actual, $message = '') + { + self::assertThat($actual, self::isEmpty(), $message); + } + + /** + * Asserts that a static attribute of a class or an attribute of an object + * is empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertEmpty( + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that a variable is not empty. + * + * @param mixed $actual + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertNotEmpty($actual, $message = '') + { + self::assertThat($actual, self::logicalNot(self::isEmpty()), $message); + } + + /** + * Asserts that a static attribute of a class or an attribute of an object + * is not empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') + { + self::assertNotEmpty( + self::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that a value is greater than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertGreaterThan($expected, $actual, $message = '') + { + self::assertThat($actual, self::greaterThan($expected), $message); + } + + /** + * Asserts that an attribute is greater than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertGreaterThan( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is greater than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertGreaterThanOrEqual($expected, $actual, $message = '') + { + self::assertThat( + $actual, + self::greaterThanOrEqual($expected), + $message + ); + } + + /** + * Asserts that an attribute is greater than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertGreaterThanOrEqual( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is smaller than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertLessThan($expected, $actual, $message = '') + { + self::assertThat($actual, self::lessThan($expected), $message); + } + + /** + * Asserts that an attribute is smaller than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertLessThan( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is smaller than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertLessThanOrEqual($expected, $actual, $message = '') + { + self::assertThat($actual, self::lessThanOrEqual($expected), $message); + } + + /** + * Asserts that an attribute is smaller than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertLessThanOrEqual( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.2.14 + */ + public static function assertFileEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false) + { + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); + + self::assertEquals( + file_get_contents($expected), + file_get_contents($actual), + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.2.14 + */ + public static function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false) + { + self::assertFileExists($expected, $message); + self::assertFileExists($actual, $message); + + self::assertNotEquals( + file_get_contents($expected), + file_get_contents($actual), + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of a string is equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.3.0 + */ + public static function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false) + { + self::assertFileExists($expectedFile, $message); + + self::assertEquals( + file_get_contents($expectedFile), + $actualString, + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of a string is not equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.3.0 + */ + public static function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false) + { + self::assertFileExists($expectedFile, $message); + + self::assertNotEquals( + file_get_contents($expectedFile), + $actualString, + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that a file exists. + * + * @param string $filename + * @param string $message + * + * @since Method available since Release 3.0.0 + */ + public static function assertFileExists($filename, $message = '') + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_FileExists; + + self::assertThat($filename, $constraint, $message); + } + + /** + * Asserts that a file does not exist. + * + * @param string $filename + * @param string $message + * + * @since Method available since Release 3.0.0 + */ + public static function assertFileNotExists($filename, $message = '') + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_FileExists + ); + + self::assertThat($filename, $constraint, $message); + } + + /** + * Asserts that a condition is true. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertTrue($condition, $message = '') + { + self::assertThat($condition, self::isTrue(), $message); + } + + /** + * Asserts that a condition is not true. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertNotTrue($condition, $message = '') + { + self::assertThat($condition, self::logicalNot(self::isTrue()), $message); + } + + /** + * Asserts that a condition is false. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertFalse($condition, $message = '') + { + self::assertThat($condition, self::isFalse(), $message); + } + + /** + * Asserts that a condition is not false. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function assertNotFalse($condition, $message = '') + { + self::assertThat($condition, self::logicalNot(self::isFalse()), $message); + } + + /** + * Asserts that a variable is not null. + * + * @param mixed $actual + * @param string $message + */ + public static function assertNotNull($actual, $message = '') + { + self::assertThat($actual, self::logicalNot(self::isNull()), $message); + } + + /** + * Asserts that a variable is null. + * + * @param mixed $actual + * @param string $message + */ + public static function assertNull($actual, $message = '') + { + self::assertThat($actual, self::isNull(), $message); + } + + /** + * Asserts that a class has a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertClassHasAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!is_string($className) || !class_exists($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className); + } + + $constraint = new PHPUnit_Framework_Constraint_ClassHasAttribute( + $attributeName + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that a class does not have a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertClassNotHasAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!is_string($className) || !class_exists($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ClassHasAttribute($attributeName) + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that a class has a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertClassHasStaticAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!is_string($className) || !class_exists($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className); + } + + $constraint = new PHPUnit_Framework_Constraint_ClassHasStaticAttribute( + $attributeName + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that a class does not have a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertClassNotHasStaticAttribute($attributeName, $className, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!is_string($className) || !class_exists($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name', $className); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ClassHasStaticAttribute( + $attributeName + ) + ); + + self::assertThat($className, $constraint, $message); + } + + /** + * Asserts that an object has a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * + * @since Method available since Release 3.0.0 + */ + public static function assertObjectHasAttribute($attributeName, $object, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!is_object($object)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object'); + } + + $constraint = new PHPUnit_Framework_Constraint_ObjectHasAttribute( + $attributeName + ); + + self::assertThat($object, $constraint, $message); + } + + /** + * Asserts that an object does not have a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * + * @since Method available since Release 3.0.0 + */ + public static function assertObjectNotHasAttribute($attributeName, $object, $message = '') + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!is_object($object)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'object'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_ObjectHasAttribute($attributeName) + ); + + self::assertThat($object, $constraint, $message); + } + + /** + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ + public static function assertSame($expected, $actual, $message = '') + { + if (is_bool($expected) && is_bool($actual)) { + self::assertEquals($expected, $actual, $message); + } else { + $constraint = new PHPUnit_Framework_Constraint_IsIdentical( + $expected + ); + + self::assertThat($actual, $constraint, $message); + } + } + + /** + * Asserts that a variable and an attribute of an object have the same type + * and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ + public static function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertSame( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ + public static function assertNotSame($expected, $actual, $message = '') + { + if (is_bool($expected) && is_bool($actual)) { + self::assertNotEquals($expected, $actual, $message); + } else { + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsIdentical($expected) + ); + + self::assertThat($actual, $constraint, $message); + } + } + + /** + * Asserts that a variable and an attribute of an object do not have the + * same type and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ + public static function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') + { + self::assertNotSame( + $expected, + self::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertInstanceOf($expected, $actual, $message = '') + { + if (!(is_string($expected) && (class_exists($expected) || interface_exists($expected)))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class or interface name'); + } + + $constraint = new PHPUnit_Framework_Constraint_IsInstanceOf( + $expected + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '') + { + self::assertInstanceOf( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertNotInstanceOf($expected, $actual, $message = '') + { + if (!(is_string($expected) && (class_exists($expected) || interface_exists($expected)))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class or interface name'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsInstanceOf($expected) + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '') + { + self::assertNotInstanceOf( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertInternalType($expected, $actual, $message = '') + { + if (!is_string($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_IsType( + $expected + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '') + { + self::assertInternalType( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertNotInternalType($expected, $actual, $message = '') + { + if (!is_string($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_IsType($expected) + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '') + { + self::assertNotInternalType( + $expected, + self::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a string matches a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + */ + public static function assertRegExp($pattern, $string, $message = '') + { + if (!is_string($pattern)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_PCREMatch($pattern); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string does not match a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + * + * @since Method available since Release 2.1.0 + */ + public static function assertNotRegExp($pattern, $string, $message = '') + { + if (!is_string($pattern)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_PCREMatch($pattern) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. + * + * @param array|Countable|Traversable $expected + * @param array|Countable|Traversable $actual + * @param string $message + */ + public static function assertSameSize($expected, $actual, $message = '') + { + if (!$expected instanceof Countable && + !$expected instanceof Traversable && + !is_array($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable or traversable'); + } + + if (!$actual instanceof Countable && + !$actual instanceof Traversable && + !is_array($actual)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable'); + } + + self::assertThat( + $actual, + new PHPUnit_Framework_Constraint_SameSize($expected), + $message + ); + } + + /** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is not the same. + * + * @param array|Countable|Traversable $expected + * @param array|Countable|Traversable $actual + * @param string $message + */ + public static function assertNotSameSize($expected, $actual, $message = '') + { + if (!$expected instanceof Countable && + !$expected instanceof Traversable && + !is_array($expected)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'countable or traversable'); + } + + if (!$actual instanceof Countable && + !$actual instanceof Traversable && + !is_array($actual)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'countable or traversable'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_SameSize($expected) + ); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a string matches a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertStringMatchesFormat($format, $string, $message = '') + { + if (!is_string($format)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringMatches($format); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string does not match a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertStringNotMatchesFormat($format, $string, $message = '') + { + if (!is_string($format)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringMatches($format) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string matches a given format file. + * + * @param string $formatFile + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertStringMatchesFormatFile($formatFile, $string, $message = '') + { + self::assertFileExists($formatFile, $message); + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringMatches( + file_get_contents($formatFile) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string does not match a given format string. + * + * @param string $formatFile + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ + public static function assertStringNotMatchesFormatFile($formatFile, $string, $message = '') + { + self::assertFileExists($formatFile, $message); + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringMatches( + file_get_contents($formatFile) + ) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string starts with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ + public static function assertStringStartsWith($prefix, $string, $message = '') + { + if (!is_string($prefix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringStartsWith( + $prefix + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ + public static function assertStringStartsNotWith($prefix, $string, $message = '') + { + if (!is_string($prefix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringStartsWith($prefix) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string ends with a given suffix. + * + * @param string $suffix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ + public static function assertStringEndsWith($suffix, $string, $message = '') + { + if (!is_string($suffix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_StringEndsWith($suffix); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that a string ends not with a given suffix. + * + * @param string $suffix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ + public static function assertStringEndsNotWith($suffix, $string, $message = '') + { + if (!is_string($suffix)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!is_string($string)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + $constraint = new PHPUnit_Framework_Constraint_Not( + new PHPUnit_Framework_Constraint_StringEndsWith($suffix) + ); + + self::assertThat($string, $constraint, $message); + } + + /** + * Asserts that two XML files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '') + { + $expected = PHPUnit_Util_XML::loadFile($expectedFile); + $actual = PHPUnit_Util_XML::loadFile($actualFile); + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '') + { + $expected = PHPUnit_Util_XML::loadFile($expectedFile); + $actual = PHPUnit_Util_XML::loadFile($actualFile); + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.3.0 + */ + public static function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '') + { + $expected = PHPUnit_Util_XML::loadFile($expectedFile); + $actual = PHPUnit_Util_XML::load($actualXml); + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are not equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.3.0 + */ + public static function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '') + { + $expected = PHPUnit_Util_XML::loadFile($expectedFile); + $actual = PHPUnit_Util_XML::load($actualXml); + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '') + { + $expected = PHPUnit_Util_XML::load($expectedXml); + $actual = PHPUnit_Util_XML::load($actualXml); + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are not equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.1.0 + */ + public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '') + { + $expected = PHPUnit_Util_XML::load($expectedXml); + $actual = PHPUnit_Util_XML::load($actualXml); + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that a hierarchy of DOMElements matches. + * + * @param DOMElement $expectedElement + * @param DOMElement $actualElement + * @param bool $checkAttributes + * @param string $message + * + * @since Method available since Release 3.3.0 + */ + public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = false, $message = '') + { + $tmp = new DOMDocument; + $expectedElement = $tmp->importNode($expectedElement, true); + + $tmp = new DOMDocument; + $actualElement = $tmp->importNode($actualElement, true); + + unset($tmp); + + self::assertEquals( + $expectedElement->tagName, + $actualElement->tagName, + $message + ); + + if ($checkAttributes) { + self::assertEquals( + $expectedElement->attributes->length, + $actualElement->attributes->length, + sprintf( + '%s%sNumber of attributes on node "%s" does not match', + $message, + !empty($message) ? "\n" : '', + $expectedElement->tagName + ) + ); + + for ($i = 0; $i < $expectedElement->attributes->length; $i++) { + $expectedAttribute = $expectedElement->attributes->item($i); + $actualAttribute = $actualElement->attributes->getNamedItem( + $expectedAttribute->name + ); + + if (!$actualAttribute) { + self::fail( + sprintf( + '%s%sCould not find attribute "%s" on node "%s"', + $message, + !empty($message) ? "\n" : '', + $expectedAttribute->name, + $expectedElement->tagName + ) + ); + } + } + } + + PHPUnit_Util_XML::removeCharacterDataNodes($expectedElement); + PHPUnit_Util_XML::removeCharacterDataNodes($actualElement); + + self::assertEquals( + $expectedElement->childNodes->length, + $actualElement->childNodes->length, + sprintf( + '%s%sNumber of child nodes of "%s" differs', + $message, + !empty($message) ? "\n" : '', + $expectedElement->tagName + ) + ); + + for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { + self::assertEqualXMLStructure( + $expectedElement->childNodes->item($i), + $actualElement->childNodes->item($i), + $checkAttributes, + $message + ); + } + } + + /** + * Assert the presence, absence, or count of elements in a document matching + * the CSS $selector, regardless of the contents of those elements. + * + * The first argument, $selector, is the CSS selector used to match + * the elements in the $actual document. + * + * The second argument, $count, can be either boolean or numeric. + * When boolean, it asserts for presence of elements matching the selector + * (true) or absence of elements (false). + * When numeric, it asserts the count of elements. + * + * assertSelectCount("#binder", true, $xml); // any? + * assertSelectCount(".binder", 3, $xml); // exactly 3? + * + * @param array $selector + * @param int|bool|array $count + * @param mixed $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + * @deprecated + * @codeCoverageIgnore + */ + public static function assertSelectCount($selector, $count, $actual, $message = '', $isHtml = true) + { + trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED); + + self::assertSelectEquals( + $selector, + true, + $count, + $actual, + $message, + $isHtml + ); + } + + /** + * assertSelectRegExp("#binder .name", "/Mike|Derek/", true, $xml); // any? + * assertSelectRegExp("#binder .name", "/Mike|Derek/", 3, $xml); // 3? + * + * @param array $selector + * @param string $pattern + * @param int|bool|array $count + * @param mixed $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + * @deprecated + * @codeCoverageIgnore + */ + public static function assertSelectRegExp($selector, $pattern, $count, $actual, $message = '', $isHtml = true) + { + trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED); + + self::assertSelectEquals( + $selector, + "regexp:$pattern", + $count, + $actual, + $message, + $isHtml + ); + } + + /** + * assertSelectEquals("#binder .name", "Chuck", true, $xml); // any? + * assertSelectEquals("#binder .name", "Chuck", false, $xml); // none? + * + * @param array $selector + * @param string $content + * @param int|bool|array $count + * @param mixed $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + * @deprecated + * @codeCoverageIgnore + */ + public static function assertSelectEquals($selector, $content, $count, $actual, $message = '', $isHtml = true) + { + trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED); + + $tags = PHPUnit_Util_XML::cssSelect( + $selector, + $content, + $actual, + $isHtml + ); + + // assert specific number of elements + if (is_numeric($count)) { + $counted = $tags ? count($tags) : 0; + self::assertEquals($count, $counted, $message); + } // assert any elements exist if true, assert no elements exist if false + elseif (is_bool($count)) { + $any = count($tags) > 0 && $tags[0] instanceof DOMNode; + + if ($count) { + self::assertTrue($any, $message); + } else { + self::assertFalse($any, $message); + } + } // check for range number of elements + elseif (is_array($count) && + (isset($count['>']) || isset($count['<']) || + isset($count['>=']) || isset($count['<=']))) { + $counted = $tags ? count($tags) : 0; + + if (isset($count['>'])) { + self::assertTrue($counted > $count['>'], $message); + } + + if (isset($count['>='])) { + self::assertTrue($counted >= $count['>='], $message); + } + + if (isset($count['<'])) { + self::assertTrue($counted < $count['<'], $message); + } + + if (isset($count['<='])) { + self::assertTrue($counted <= $count['<='], $message); + } + } else { + throw new PHPUnit_Framework_Exception; + } + } + + /** + * Evaluate an HTML or XML string and assert its structure and/or contents. + * + * The first argument ($matcher) is an associative array that specifies the + * match criteria for the assertion: + * + * - `id` : the node with the given id attribute must match the + * corresponding value. + * - `tag` : the node type must match the corresponding value. + * - `attributes` : a hash. The node's attributes must match the + * corresponding values in the hash. + * - `content` : The text content must match the given value. + * - `parent` : a hash. The node's parent must match the + * corresponding hash. + * - `child` : a hash. At least one of the node's immediate children + * must meet the criteria described by the hash. + * - `ancestor` : a hash. At least one of the node's ancestors must + * meet the criteria described by the hash. + * - `descendant` : a hash. At least one of the node's descendants must + * meet the criteria described by the hash. + * - `children` : a hash, for counting children of a node. + * Accepts the keys: + * - `count` : a number which must equal the number of children + * that match + * - `less_than` : the number of matching children must be greater + * than this number + * - `greater_than` : the number of matching children must be less than + * this number + * - `only` : another hash consisting of the keys to use to match + * on the children, and only matching children will be + * counted + * + * + * // Matcher that asserts that there is an element with an id="my_id". + * $matcher = array('id' => 'my_id'); + * + * // Matcher that asserts that there is a "span" tag. + * $matcher = array('tag' => 'span'); + * + * // Matcher that asserts that there is a "span" tag with the content + * // "Hello World". + * $matcher = array('tag' => 'span', 'content' => 'Hello World'); + * + * // Matcher that asserts that there is a "span" tag with content matching + * // the regular expression pattern. + * $matcher = array('tag' => 'span', 'content' => 'regexp:/Try P(HP|ython)/'); + * + * // Matcher that asserts that there is a "span" with an "list" class + * // attribute. + * $matcher = array( + * 'tag' => 'span', + * 'attributes' => array('class' => 'list') + * ); + * + * // Matcher that asserts that there is a "span" inside of a "div". + * $matcher = array( + * 'tag' => 'span', + * 'parent' => array('tag' => 'div') + * ); + * + * // Matcher that asserts that there is a "span" somewhere inside a + * // "table". + * $matcher = array( + * 'tag' => 'span', + * 'ancestor' => array('tag' => 'table') + * ); + * + * // Matcher that asserts that there is a "span" with at least one "em" + * // child. + * $matcher = array( + * 'tag' => 'span', + * 'child' => array('tag' => 'em') + * ); + * + * // Matcher that asserts that there is a "span" containing a (possibly + * // nested) "strong" tag. + * $matcher = array( + * 'tag' => 'span', + * 'descendant' => array('tag' => 'strong') + * ); + * + * // Matcher that asserts that there is a "span" containing 5-10 "em" tags + * // as immediate children. + * $matcher = array( + * 'tag' => 'span', + * 'children' => array( + * 'less_than' => 11, + * 'greater_than' => 4, + * 'only' => array('tag' => 'em') + * ) + * ); + * + * // Matcher that asserts that there is a "div", with an "ul" ancestor and + * // a "li" parent (with class="enum"), and containing a "span" descendant + * // that contains an element with id="my_test" and the text "Hello World". + * $matcher = array( + * 'tag' => 'div', + * 'ancestor' => array('tag' => 'ul'), + * 'parent' => array( + * 'tag' => 'li', + * 'attributes' => array('class' => 'enum') + * ), + * 'descendant' => array( + * 'tag' => 'span', + * 'child' => array( + * 'id' => 'my_test', + * 'content' => 'Hello World' + * ) + * ) + * ); + * + * // Use assertTag() to apply a $matcher to a piece of $html. + * $this->assertTag($matcher, $html); + * + * // Use assertTag() to apply a $matcher to a piece of $xml. + * $this->assertTag($matcher, $xml, '', false); + * + * + * The second argument ($actual) is a string containing either HTML or + * XML text to be tested. + * + * The third argument ($message) is an optional message that will be + * used if the assertion fails. + * + * The fourth argument ($html) is an optional flag specifying whether + * to load the $actual string into a DOMDocument using the HTML or + * XML load strategy. It is true by default, which assumes the HTML + * load strategy. In many cases, this will be acceptable for XML as well. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + * @deprecated + * @codeCoverageIgnore + */ + public static function assertTag($matcher, $actual, $message = '', $isHtml = true) + { + trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED); + + $dom = PHPUnit_Util_XML::load($actual, $isHtml); + $tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $isHtml); + $matched = count($tags) > 0 && $tags[0] instanceof DOMNode; + + self::assertTrue($matched, $message); + } + + /** + * This assertion is the exact opposite of assertTag(). + * + * Rather than asserting that $matcher results in a match, it asserts that + * $matcher does not match. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + * @deprecated + * @codeCoverageIgnore + */ + public static function assertNotTag($matcher, $actual, $message = '', $isHtml = true) + { + trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED); + + $dom = PHPUnit_Util_XML::load($actual, $isHtml); + $tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $isHtml); + $matched = count($tags) > 0 && $tags[0] instanceof DOMNode; + + self::assertFalse($matched, $message); + } + + /** + * Evaluates a PHPUnit_Framework_Constraint matcher object. + * + * @param mixed $value + * @param PHPUnit_Framework_Constraint $constraint + * @param string $message + * + * @since Method available since Release 3.0.0 + */ + public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '') + { + self::$count += count($constraint); + + $constraint->evaluate($value, $message); + } + + /** + * Asserts that a string is a valid JSON string. + * + * @param string $actualJson + * @param string $message + * + * @since Method available since Release 3.7.20 + */ + public static function assertJson($actualJson, $message = '') + { + if (!is_string($actualJson)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + self::assertThat($actualJson, self::isJson(), $message); + } + + /** + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '') + { + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); + + $expected = json_decode($expectedJson); + $actual = json_decode($actualJson); + + self::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '') + { + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); + + $expected = json_decode($expectedJson); + $actual = json_decode($actualJson); + + self::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '') + { + self::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); + + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); + + // call constraint + $constraint = new PHPUnit_Framework_Constraint_JsonMatches( + $expectedJson + ); + + self::assertThat($actualJson, $constraint, $message); + } + + /** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + */ + public static function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '') + { + self::assertFileExists($expectedFile, $message); + $expectedJson = file_get_contents($expectedFile); + + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); + + // call constraint + $constraint = new PHPUnit_Framework_Constraint_JsonMatches( + $expectedJson + ); + + self::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraint), $message); + } + + /** + * Asserts that two JSON files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + */ + public static function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '') + { + self::assertFileExists($expectedFile, $message); + self::assertFileExists($actualFile, $message); + + $actualJson = file_get_contents($actualFile); + $expectedJson = file_get_contents($expectedFile); + + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); + + // call constraint + $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches( + $expectedJson + ); + + $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson); + + self::assertThat($expectedJson, new PHPUnit_Framework_Constraint_Not($constraintActual), $message); + self::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraintExpected), $message); + } + + /** + * Asserts that two JSON files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + */ + public static function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '') + { + self::assertFileExists($expectedFile, $message); + self::assertFileExists($actualFile, $message); + + $actualJson = file_get_contents($actualFile); + $expectedJson = file_get_contents($expectedFile); + + self::assertJson($expectedJson, $message); + self::assertJson($actualJson, $message); + + // call constraint + $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches( + $expectedJson + ); + + $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson); + + self::assertThat($expectedJson, $constraintActual, $message); + self::assertThat($actualJson, $constraintExpected, $message); + } + + /** + * Returns a PHPUnit_Framework_Constraint_And matcher object. + * + * @return PHPUnit_Framework_Constraint_And + * + * @since Method available since Release 3.0.0 + */ + public static function logicalAnd() + { + $constraints = func_get_args(); + + $constraint = new PHPUnit_Framework_Constraint_And; + $constraint->setConstraints($constraints); + + return $constraint; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Or matcher object. + * + * @return PHPUnit_Framework_Constraint_Or + * + * @since Method available since Release 3.0.0 + */ + public static function logicalOr() + { + $constraints = func_get_args(); + + $constraint = new PHPUnit_Framework_Constraint_Or; + $constraint->setConstraints($constraints); + + return $constraint; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Not matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * + * @return PHPUnit_Framework_Constraint_Not + * + * @since Method available since Release 3.0.0 + */ + public static function logicalNot(PHPUnit_Framework_Constraint $constraint) + { + return new PHPUnit_Framework_Constraint_Not($constraint); + } + + /** + * Returns a PHPUnit_Framework_Constraint_Xor matcher object. + * + * @return PHPUnit_Framework_Constraint_Xor + * + * @since Method available since Release 3.0.0 + */ + public static function logicalXor() + { + $constraints = func_get_args(); + + $constraint = new PHPUnit_Framework_Constraint_Xor; + $constraint->setConstraints($constraints); + + return $constraint; + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object. + * + * @return PHPUnit_Framework_Constraint_IsAnything + * + * @since Method available since Release 3.0.0 + */ + public static function anything() + { + return new PHPUnit_Framework_Constraint_IsAnything; + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object. + * + * @return PHPUnit_Framework_Constraint_IsTrue + * + * @since Method available since Release 3.3.0 + */ + public static function isTrue() + { + return new PHPUnit_Framework_Constraint_IsTrue; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Callback matcher object. + * + * @param callable $callback + * + * @return PHPUnit_Framework_Constraint_Callback + */ + public static function callback($callback) + { + return new PHPUnit_Framework_Constraint_Callback($callback); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object. + * + * @return PHPUnit_Framework_Constraint_IsFalse + * + * @since Method available since Release 3.3.0 + */ + public static function isFalse() + { + return new PHPUnit_Framework_Constraint_IsFalse; + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsJson matcher object. + * + * @return PHPUnit_Framework_Constraint_IsJson + * + * @since Method available since Release 3.7.20 + */ + public static function isJson() + { + return new PHPUnit_Framework_Constraint_IsJson; + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsNull matcher object. + * + * @return PHPUnit_Framework_Constraint_IsNull + * + * @since Method available since Release 3.3.0 + */ + public static function isNull() + { + return new PHPUnit_Framework_Constraint_IsNull; + } + + /** + * Returns a PHPUnit_Framework_Constraint_Attribute matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_Attribute + * + * @since Method available since Release 3.1.0 + */ + public static function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName) + { + return new PHPUnit_Framework_Constraint_Attribute( + $constraint, + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher + * object. + * + * @param mixed $value + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @return PHPUnit_Framework_Constraint_TraversableContains + * + * @since Method available since Release 3.0.0 + */ + public static function contains($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) + { + return new PHPUnit_Framework_Constraint_TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity); + } + + /** + * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher + * object. + * + * @param string $type + * + * @return PHPUnit_Framework_Constraint_TraversableContainsOnly + * + * @since Method available since Release 3.1.4 + */ + public static function containsOnly($type) + { + return new PHPUnit_Framework_Constraint_TraversableContainsOnly($type); + } + + /** + * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher + * object. + * + * @param string $classname + * + * @return PHPUnit_Framework_Constraint_TraversableContainsOnly + */ + public static function containsOnlyInstancesOf($classname) + { + return new PHPUnit_Framework_Constraint_TraversableContainsOnly($classname, false); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object. + * + * @param mixed $key + * + * @return PHPUnit_Framework_Constraint_ArrayHasKey + * + * @since Method available since Release 3.0.0 + */ + public static function arrayHasKey($key) + { + return new PHPUnit_Framework_Constraint_ArrayHasKey($key); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object. + * + * @param mixed $value + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @return PHPUnit_Framework_Constraint_IsEqual + * + * @since Method available since Release 3.0.0 + */ + public static function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + return new PHPUnit_Framework_Constraint_IsEqual( + $value, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object + * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher + * object. + * + * @param string $attributeName + * @param mixed $value + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @return PHPUnit_Framework_Constraint_Attribute + * + * @since Method available since Release 3.1.0 + */ + public static function attributeEqualTo($attributeName, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + return self::attribute( + self::equalTo( + $value, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ), + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object. + * + * @return PHPUnit_Framework_Constraint_IsEmpty + * + * @since Method available since Release 3.5.0 + */ + public static function isEmpty() + { + return new PHPUnit_Framework_Constraint_IsEmpty; + } + + /** + * Returns a PHPUnit_Framework_Constraint_FileExists matcher object. + * + * @return PHPUnit_Framework_Constraint_FileExists + * + * @since Method available since Release 3.0.0 + */ + public static function fileExists() + { + return new PHPUnit_Framework_Constraint_FileExists; + } + + /** + * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_GreaterThan + * + * @since Method available since Release 3.0.0 + */ + public static function greaterThan($value) + { + return new PHPUnit_Framework_Constraint_GreaterThan($value); + } + + /** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_Or + * + * @since Method available since Release 3.1.0 + */ + public static function greaterThanOrEqual($value) + { + return self::logicalOr( + new PHPUnit_Framework_Constraint_IsEqual($value), + new PHPUnit_Framework_Constraint_GreaterThan($value) + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object. + * + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_ClassHasAttribute + * + * @since Method available since Release 3.1.0 + */ + public static function classHasAttribute($attributeName) + { + return new PHPUnit_Framework_Constraint_ClassHasAttribute( + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher + * object. + * + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * + * @since Method available since Release 3.1.0 + */ + public static function classHasStaticAttribute($attributeName) + { + return new PHPUnit_Framework_Constraint_ClassHasStaticAttribute( + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object. + * + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_ObjectHasAttribute + * + * @since Method available since Release 3.0.0 + */ + public static function objectHasAttribute($attributeName) + { + return new PHPUnit_Framework_Constraint_ObjectHasAttribute( + $attributeName + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_IsIdentical + * + * @since Method available since Release 3.0.0 + */ + public static function identicalTo($value) + { + return new PHPUnit_Framework_Constraint_IsIdentical($value); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object. + * + * @param string $className + * + * @return PHPUnit_Framework_Constraint_IsInstanceOf + * + * @since Method available since Release 3.0.0 + */ + public static function isInstanceOf($className) + { + return new PHPUnit_Framework_Constraint_IsInstanceOf($className); + } + + /** + * Returns a PHPUnit_Framework_Constraint_IsType matcher object. + * + * @param string $type + * + * @return PHPUnit_Framework_Constraint_IsType + * + * @since Method available since Release 3.0.0 + */ + public static function isType($type) + { + return new PHPUnit_Framework_Constraint_IsType($type); + } + + /** + * Returns a PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_LessThan + * + * @since Method available since Release 3.0.0 + */ + public static function lessThan($value) + { + return new PHPUnit_Framework_Constraint_LessThan($value); + } + + /** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_Or + * + * @since Method available since Release 3.1.0 + */ + public static function lessThanOrEqual($value) + { + return self::logicalOr( + new PHPUnit_Framework_Constraint_IsEqual($value), + new PHPUnit_Framework_Constraint_LessThan($value) + ); + } + + /** + * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object. + * + * @param string $pattern + * + * @return PHPUnit_Framework_Constraint_PCREMatch + * + * @since Method available since Release 3.0.0 + */ + public static function matchesRegularExpression($pattern) + { + return new PHPUnit_Framework_Constraint_PCREMatch($pattern); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object. + * + * @param string $string + * + * @return PHPUnit_Framework_Constraint_StringMatches + * + * @since Method available since Release 3.5.0 + */ + public static function matches($string) + { + return new PHPUnit_Framework_Constraint_StringMatches($string); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object. + * + * @param mixed $prefix + * + * @return PHPUnit_Framework_Constraint_StringStartsWith + * + * @since Method available since Release 3.4.0 + */ + public static function stringStartsWith($prefix) + { + return new PHPUnit_Framework_Constraint_StringStartsWith($prefix); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringContains matcher object. + * + * @param string $string + * @param bool $case + * + * @return PHPUnit_Framework_Constraint_StringContains + * + * @since Method available since Release 3.0.0 + */ + public static function stringContains($string, $case = true) + { + return new PHPUnit_Framework_Constraint_StringContains($string, $case); + } + + /** + * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object. + * + * @param mixed $suffix + * + * @return PHPUnit_Framework_Constraint_StringEndsWith + * + * @since Method available since Release 3.4.0 + */ + public static function stringEndsWith($suffix) + { + return new PHPUnit_Framework_Constraint_StringEndsWith($suffix); + } + + /** + * Returns a PHPUnit_Framework_Constraint_Count matcher object. + * + * @param int $count + * + * @return PHPUnit_Framework_Constraint_Count + */ + public static function countOf($count) + { + return new PHPUnit_Framework_Constraint_Count($count); + } + /** + * Fails a test with the given message. + * + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ + public static function fail($message = '') + { + throw new PHPUnit_Framework_AssertionFailedError($message); + } + + /** + * Returns the value of an attribute of a class or an object. + * This also works for attributes that are declared protected or private. + * + * @param mixed $classOrObject + * @param string $attributeName + * + * @return mixed + * + * @throws PHPUnit_Framework_Exception + */ + public static function readAttribute($classOrObject, $attributeName) + { + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name'); + } + + if (is_string($classOrObject)) { + if (!class_exists($classOrObject)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'class name' + ); + } + + return self::getStaticAttribute( + $classOrObject, + $attributeName + ); + } elseif (is_object($classOrObject)) { + return self::getObjectAttribute( + $classOrObject, + $attributeName + ); + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'class name or object' + ); + } + } + + /** + * Returns the value of a static attribute. + * This also works for attributes that are declared protected or private. + * + * @param string $className + * @param string $attributeName + * + * @return mixed + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 4.0.0 + */ + public static function getStaticAttribute($className, $attributeName) + { + if (!is_string($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (!class_exists($className)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class name'); + } + + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name'); + } + + $class = new ReflectionClass($className); + + while ($class) { + $attributes = $class->getStaticProperties(); + + if (array_key_exists($attributeName, $attributes)) { + return $attributes[$attributeName]; + } + + $class = $class->getParentClass(); + } + + throw new PHPUnit_Framework_Exception( + sprintf( + 'Attribute "%s" not found in class.', + $attributeName + ) + ); + } + + /** + * Returns the value of an object's attribute. + * This also works for attributes that are declared protected or private. + * + * @param object $object + * @param string $attributeName + * + * @return mixed + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 4.0.0 + */ + public static function getObjectAttribute($object, $attributeName) + { + if (!is_object($object)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'object'); + } + + if (!is_string($attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); + } + + if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name'); + } + + try { + $attribute = new ReflectionProperty($object, $attributeName); + } catch (ReflectionException $e) { + $reflector = new ReflectionObject($object); + + while ($reflector = $reflector->getParentClass()) { + try { + $attribute = $reflector->getProperty($attributeName); + break; + } catch (ReflectionException $e) { + } + } + } + + if (isset($attribute)) { + if (!$attribute || $attribute->isPublic()) { + return $object->$attributeName; + } + + $attribute->setAccessible(true); + $value = $attribute->getValue($object); + $attribute->setAccessible(false); + + return $value; + } + + throw new PHPUnit_Framework_Exception( + sprintf( + 'Attribute "%s" not found in object.', + $attributeName + ) + ); + } + + /** + * Mark the test as incomplete. + * + * @param string $message + * + * @throws PHPUnit_Framework_IncompleteTestError + * + * @since Method available since Release 3.0.0 + */ + public static function markTestIncomplete($message = '') + { + throw new PHPUnit_Framework_IncompleteTestError($message); + } + + /** + * Mark the test as skipped. + * + * @param string $message + * + * @throws PHPUnit_Framework_SkippedTestError + * + * @since Method available since Release 3.0.0 + */ + public static function markTestSkipped($message = '') + { + throw new PHPUnit_Framework_SkippedTestError($message); + } + + /** + * Return the current assertion count. + * + * @return int + * + * @since Method available since Release 3.3.3 + */ + public static function getCount() + { + return self::$count; + } + + /** + * Reset the assertion counter. + * + * @since Method available since Release 3.3.3 + */ + public static function resetCount() + { + self::$count = 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for classes that can return a description of itself. + * + * @since Interface available since Release 3.0.0 + */ +interface PHPUnit_Framework_SelfDescribing +{ + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString(); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A marker interface for marking any exception/error as result of an unit + * test as risky. + * + * @since Interface available since Release 4.0.0 + */ +interface PHPUnit_Framework_RiskyTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a test test that unintentionally covers code. + * + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Framework_UnintentionallyCoveredCodeError extends PHPUnit_Framework_RiskyTestError +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestResult collects the results of executing a test case. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_TestResult implements Countable +{ + /** + * @var array + */ + protected $passed = array(); + + /** + * @var array + */ + protected $errors = array(); + + /** + * @var array + */ + protected $failures = array(); + + /** + * @var array + */ + protected $notImplemented = array(); + + /** + * @var array + */ + protected $risky = array(); + + /** + * @var array + */ + protected $skipped = array(); + + /** + * @var array + */ + protected $listeners = array(); + + /** + * @var int + */ + protected $runTests = 0; + + /** + * @var float + */ + protected $time = 0; + + /** + * @var PHPUnit_Framework_TestSuite + */ + protected $topTestSuite = null; + + /** + * Code Coverage information. + * + * @var PHP_CodeCoverage + */ + protected $codeCoverage; + + /** + * @var bool + */ + protected $convertErrorsToExceptions = true; + + /** + * @var bool + */ + protected $stop = false; + + /** + * @var bool + */ + protected $stopOnError = false; + + /** + * @var bool + */ + protected $stopOnFailure = false; + + /** + * @var bool + */ + protected $beStrictAboutTestsThatDoNotTestAnything = false; + + /** + * @var bool + */ + protected $beStrictAboutOutputDuringTests = false; + + /** + * @var bool + */ + protected $beStrictAboutTestSize = false; + + /** + * @var bool + */ + protected $beStrictAboutTodoAnnotatedTests = false; + + /** + * @var bool + */ + protected $stopOnRisky = false; + + /** + * @var bool + */ + protected $stopOnIncomplete = false; + + /** + * @var bool + */ + protected $stopOnSkipped = false; + + /** + * @var bool + */ + protected $lastTestFailed = false; + + /** + * @var int + */ + protected $timeoutForSmallTests = 1; + + /** + * @var int + */ + protected $timeoutForMediumTests = 10; + + /** + * @var int + */ + protected $timeoutForLargeTests = 60; + + /** + * Registers a TestListener. + * + * @param PHPUnit_Framework_TestListener + */ + public function addListener(PHPUnit_Framework_TestListener $listener) + { + $this->listeners[] = $listener; + } + + /** + * Unregisters a TestListener. + * + * @param PHPUnit_Framework_TestListener $listener + */ + public function removeListener(PHPUnit_Framework_TestListener $listener) + { + foreach ($this->listeners as $key => $_listener) { + if ($listener === $_listener) { + unset($this->listeners[$key]); + } + } + } + + /** + * Flushes all flushable TestListeners. + * + * @since Method available since Release 3.0.0 + */ + public function flushListeners() + { + foreach ($this->listeners as $listener) { + if ($listener instanceof PHPUnit_Util_Printer) { + $listener->flush(); + } + } + } + + /** + * Adds an error to the list of errors. + * + * @param PHPUnit_Framework_Test $test + * @param Exception $e + * @param float $time + */ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + if ($e instanceof PHPUnit_Framework_RiskyTest) { + $this->risky[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addRiskyTest'; + + if ($this->stopOnRisky) { + $this->stop(); + } + } elseif ($e instanceof PHPUnit_Framework_IncompleteTest) { + $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addIncompleteTest'; + + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($e instanceof PHPUnit_Framework_SkippedTest) { + $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addSkippedTest'; + + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->errors[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addError'; + + if ($this->stopOnError || $this->stopOnFailure) { + $this->stop(); + } + } + + foreach ($this->listeners as $listener) { + $listener->$notifyMethod($test, $e, $time); + } + + $this->lastTestFailed = true; + $this->time += $time; + } + + /** + * Adds a failure to the list of failures. + * The passed in exception caused the failure. + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_AssertionFailedError $e + * @param float $time + */ + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + if ($e instanceof PHPUnit_Framework_RiskyTest || + $e instanceof PHPUnit_Framework_OutputError) { + $this->risky[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addRiskyTest'; + + if ($this->stopOnRisky) { + $this->stop(); + } + } elseif ($e instanceof PHPUnit_Framework_IncompleteTest) { + $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addIncompleteTest'; + + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($e instanceof PHPUnit_Framework_SkippedTest) { + $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addSkippedTest'; + + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e); + $notifyMethod = 'addFailure'; + + if ($this->stopOnFailure) { + $this->stop(); + } + } + + foreach ($this->listeners as $listener) { + $listener->$notifyMethod($test, $e, $time); + } + + $this->lastTestFailed = true; + $this->time += $time; + } + + /** + * Informs the result that a testsuite will be started. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + if ($this->topTestSuite === null) { + $this->topTestSuite = $suite; + } + + foreach ($this->listeners as $listener) { + $listener->startTestSuite($suite); + } + } + + /** + * Informs the result that a testsuite was completed. + * + * @param PHPUnit_Framework_TestSuite $suite + * + * @since Method available since Release 2.2.0 + */ + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + foreach ($this->listeners as $listener) { + $listener->endTestSuite($suite); + } + } + + /** + * Informs the result that a test will be started. + * + * @param PHPUnit_Framework_Test $test + */ + public function startTest(PHPUnit_Framework_Test $test) + { + $this->lastTestFailed = false; + $this->runTests += count($test); + + foreach ($this->listeners as $listener) { + $listener->startTest($test); + } + } + + /** + * Informs the result that a test was completed. + * + * @param PHPUnit_Framework_Test $test + * @param float $time + */ + public function endTest(PHPUnit_Framework_Test $test, $time) + { + foreach ($this->listeners as $listener) { + $listener->endTest($test, $time); + } + + if (!$this->lastTestFailed && $test instanceof PHPUnit_Framework_TestCase) { + $class = get_class($test); + $key = $class . '::' . $test->getName(); + + $this->passed[$key] = array( + 'result' => $test->getResult(), + 'size' => PHPUnit_Util_Test::getSize( + $class, + $test->getName(false) + ) + ); + + $this->time += $time; + } + } + + /** + * Returns true if no risky test occurred. + * + * @return bool + * + * @since Method available since Release 4.0.0 + */ + public function allHarmless() + { + return $this->riskyCount() == 0; + } + + /** + * Gets the number of risky tests. + * + * @return int + * + * @since Method available since Release 4.0.0 + */ + public function riskyCount() + { + return count($this->risky); + } + + /** + * Returns true if no incomplete test occurred. + * + * @return bool + */ + public function allCompletelyImplemented() + { + return $this->notImplementedCount() == 0; + } + + /** + * Gets the number of incomplete tests. + * + * @return int + */ + public function notImplementedCount() + { + return count($this->notImplemented); + } + + /** + * Returns an Enumeration for the risky tests. + * + * @return array + * + * @since Method available since Release 4.0.0 + */ + public function risky() + { + return $this->risky; + } + + /** + * Returns an Enumeration for the incomplete tests. + * + * @return array + */ + public function notImplemented() + { + return $this->notImplemented; + } + + /** + * Returns true if no test has been skipped. + * + * @return bool + * + * @since Method available since Release 3.0.0 + */ + public function noneSkipped() + { + return $this->skippedCount() == 0; + } + + /** + * Gets the number of skipped tests. + * + * @return int + * + * @since Method available since Release 3.0.0 + */ + public function skippedCount() + { + return count($this->skipped); + } + + /** + * Returns an Enumeration for the skipped tests. + * + * @return array + * + * @since Method available since Release 3.0.0 + */ + public function skipped() + { + return $this->skipped; + } + + /** + * Gets the number of detected errors. + * + * @return int + */ + public function errorCount() + { + return count($this->errors); + } + + /** + * Returns an Enumeration for the errors. + * + * @return array + */ + public function errors() + { + return $this->errors; + } + + /** + * Gets the number of detected failures. + * + * @return int + */ + public function failureCount() + { + return count($this->failures); + } + + /** + * Returns an Enumeration for the failures. + * + * @return array + */ + public function failures() + { + return $this->failures; + } + + /** + * Returns the names of the tests that have passed. + * + * @return array + * + * @since Method available since Release 3.4.0 + */ + public function passed() + { + return $this->passed; + } + + /** + * Returns the (top) test suite. + * + * @return PHPUnit_Framework_TestSuite + * + * @since Method available since Release 3.0.0 + */ + public function topTestSuite() + { + return $this->topTestSuite; + } + + /** + * Returns whether code coverage information should be collected. + * + * @return bool If code coverage should be collected + * + * @since Method available since Release 3.2.0 + */ + public function getCollectCodeCoverageInformation() + { + return $this->codeCoverage !== null; + } + + /** + * Runs a TestCase. + * + * @param PHPUnit_Framework_Test $test + */ + public function run(PHPUnit_Framework_Test $test) + { + PHPUnit_Framework_Assert::resetCount(); + + $error = false; + $failure = false; + $incomplete = false; + $risky = false; + $skipped = false; + + $this->startTest($test); + + $errorHandlerSet = false; + + if ($this->convertErrorsToExceptions) { + $oldErrorHandler = set_error_handler( + array('PHPUnit_Util_ErrorHandler', 'handleError'), + E_ALL | E_STRICT + ); + + if ($oldErrorHandler === null) { + $errorHandlerSet = true; + } else { + restore_error_handler(); + } + } + + $collectCodeCoverage = $this->codeCoverage !== null && + !$test instanceof PHPUnit_Extensions_SeleniumTestCase && + !$test instanceof PHPUnit_Framework_Warning; + + if ($collectCodeCoverage) { + // We need to blacklist test source files when no whitelist is used. + if (!$this->codeCoverage->filter()->hasWhitelist()) { + $classes = $this->getHierarchy(get_class($test), true); + + foreach ($classes as $class) { + $this->codeCoverage->filter()->addFileToBlacklist( + $class->getFileName() + ); + } + } + + $this->codeCoverage->start($test); + } + + PHP_Timer::start(); + + try { + if (!$test instanceof PHPUnit_Framework_Warning && + $test->getSize() != PHPUnit_Util_Test::UNKNOWN && + $this->beStrictAboutTestSize && + extension_loaded('pcntl') && class_exists('PHP_Invoker')) { + switch ($test->getSize()) { + case PHPUnit_Util_Test::SMALL: + $_timeout = $this->timeoutForSmallTests; + break; + + case PHPUnit_Util_Test::MEDIUM: + $_timeout = $this->timeoutForMediumTests; + break; + + case PHPUnit_Util_Test::LARGE: + $_timeout = $this->timeoutForLargeTests; + break; + } + + $invoker = new PHP_Invoker; + $invoker->invoke(array($test, 'runBare'), array(), $_timeout); + } else { + $test->runBare(); + } + } catch (PHPUnit_Framework_AssertionFailedError $e) { + $failure = true; + + if ($e instanceof PHPUnit_Framework_RiskyTestError) { + $risky = true; + } elseif ($e instanceof PHPUnit_Framework_IncompleteTestError) { + $incomplete = true; + } elseif ($e instanceof PHPUnit_Framework_SkippedTestError) { + $skipped = true; + } + } catch (PHPUnit_Framework_Exception $e) { + $error = true; + } catch (Throwable $e) { + $e = new PHPUnit_Framework_ExceptionWrapper($e); + $error = true; + } catch (Exception $e) { + $e = new PHPUnit_Framework_ExceptionWrapper($e); + $error = true; + } + + $time = PHP_Timer::stop(); + $test->addToAssertionCount(PHPUnit_Framework_Assert::getCount()); + + if ($this->beStrictAboutTestsThatDoNotTestAnything && + $test->getNumAssertions() == 0) { + $risky = true; + } + + if ($collectCodeCoverage) { + $append = !$risky && !$incomplete && !$skipped; + $linesToBeCovered = array(); + $linesToBeUsed = array(); + + if ($append && $test instanceof PHPUnit_Framework_TestCase) { + $linesToBeCovered = PHPUnit_Util_Test::getLinesToBeCovered( + get_class($test), + $test->getName(false) + ); + + $linesToBeUsed = PHPUnit_Util_Test::getLinesToBeUsed( + get_class($test), + $test->getName(false) + ); + } + + try { + $this->codeCoverage->stop( + $append, + $linesToBeCovered, + $linesToBeUsed + ); + } catch (PHP_CodeCoverage_Exception_UnintentionallyCoveredCode $cce) { + $this->addFailure( + $test, + new PHPUnit_Framework_UnintentionallyCoveredCodeError( + 'This test executed code that is not listed as code to be covered or used:' . + PHP_EOL . $cce->getMessage() + ), + $time + ); + } catch (PHPUnit_Framework_InvalidCoversTargetException $cce) { + $this->addFailure( + $test, + new PHPUnit_Framework_InvalidCoversTargetError( + $cce->getMessage() + ), + $time + ); + } catch (PHP_CodeCoverage_Exception $cce) { + $error = true; + + if (!isset($e)) { + $e = $cce; + } + } + } + + if ($errorHandlerSet === true) { + restore_error_handler(); + } + + if ($error === true) { + $this->addError($test, $e, $time); + } elseif ($failure === true) { + $this->addFailure($test, $e, $time); + } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && + $test->getNumAssertions() == 0) { + $this->addFailure( + $test, + new PHPUnit_Framework_RiskyTestError( + 'This test did not perform any assertions' + ), + $time + ); + } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { + $this->addFailure( + $test, + new PHPUnit_Framework_OutputError( + sprintf( + 'This test printed output: %s', + $test->getActualOutput() + ) + ), + $time + ); + } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof PHPUnit_Framework_TestCase) { + $annotations = $test->getAnnotations(); + + if (isset($annotations['method']['todo'])) { + $this->addFailure( + $test, + new PHPUnit_Framework_RiskyTestError( + 'Test method is annotated with @todo' + ), + $time + ); + } + } + + $this->endTest($test, $time); + } + + /** + * Gets the number of run tests. + * + * @return int + */ + public function count() + { + return $this->runTests; + } + + /** + * Checks whether the test run should stop. + * + * @return bool + */ + public function shouldStop() + { + return $this->stop; + } + + /** + * Marks that the test run should stop. + */ + public function stop() + { + $this->stop = true; + } + + /** + * Returns the PHP_CodeCoverage object. + * + * @return PHP_CodeCoverage + * + * @since Method available since Release 3.5.0 + */ + public function getCodeCoverage() + { + return $this->codeCoverage; + } + + /** + * Sets the PHP_CodeCoverage object. + * + * @param PHP_CodeCoverage $codeCoverage + * + * @since Method available since Release 3.6.0 + */ + public function setCodeCoverage(PHP_CodeCoverage $codeCoverage) + { + $this->codeCoverage = $codeCoverage; + } + + /** + * Enables or disables the error-to-exception conversion. + * + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.2.14 + */ + public function convertErrorsToExceptions($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->convertErrorsToExceptions = $flag; + } + + /** + * Returns the error-to-exception conversion setting. + * + * @return bool + * + * @since Method available since Release 3.4.0 + */ + public function getConvertErrorsToExceptions() + { + return $this->convertErrorsToExceptions; + } + + /** + * Enables or disables the stopping when an error occurs. + * + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.5.0 + */ + public function stopOnError($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->stopOnError = $flag; + } + + /** + * Enables or disables the stopping when a failure occurs. + * + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.1.0 + */ + public function stopOnFailure($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->stopOnFailure = $flag; + } + + /** + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 4.0.0 + */ + public function beStrictAboutTestsThatDoNotTestAnything($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->beStrictAboutTestsThatDoNotTestAnything = $flag; + } + + /** + * @return bool + * + * @since Method available since Release 4.0.0 + */ + public function isStrictAboutTestsThatDoNotTestAnything() + { + return $this->beStrictAboutTestsThatDoNotTestAnything; + } + + /** + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 4.0.0 + */ + public function beStrictAboutOutputDuringTests($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->beStrictAboutOutputDuringTests = $flag; + } + + /** + * @return bool + * + * @since Method available since Release 4.0.0 + */ + public function isStrictAboutOutputDuringTests() + { + return $this->beStrictAboutOutputDuringTests; + } + + /** + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 4.0.0 + */ + public function beStrictAboutTestSize($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->beStrictAboutTestSize = $flag; + } + + /** + * @return bool + * + * @since Method available since Release 4.0.0 + */ + public function isStrictAboutTestSize() + { + return $this->beStrictAboutTestSize; + } + + /** + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 4.2.0 + */ + public function beStrictAboutTodoAnnotatedTests($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->beStrictAboutTodoAnnotatedTests = $flag; + } + + /** + * @return bool + * + * @since Method available since Release 4.2.0 + */ + public function isStrictAboutTodoAnnotatedTests() + { + return $this->beStrictAboutTodoAnnotatedTests; + } + + /** + * Enables or disables the stopping for risky tests. + * + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 4.0.0 + */ + public function stopOnRisky($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->stopOnRisky = $flag; + } + + /** + * Enables or disables the stopping for incomplete tests. + * + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.5.0 + */ + public function stopOnIncomplete($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->stopOnIncomplete = $flag; + } + + /** + * Enables or disables the stopping for skipped tests. + * + * @param bool $flag + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.1.0 + */ + public function stopOnSkipped($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->stopOnSkipped = $flag; + } + + /** + * Returns the time spent running the tests. + * + * @return float + */ + public function time() + { + return $this->time; + } + + /** + * Returns whether the entire test was successful or not. + * + * @return bool + */ + public function wasSuccessful() + { + return empty($this->errors) && empty($this->failures); + } + + /** + * Sets the timeout for small tests. + * + * @param int $timeout + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.6.0 + */ + public function setTimeoutForSmallTests($timeout) + { + if (!is_integer($timeout)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->timeoutForSmallTests = $timeout; + } + + /** + * Sets the timeout for medium tests. + * + * @param int $timeout + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.6.0 + */ + public function setTimeoutForMediumTests($timeout) + { + if (!is_integer($timeout)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->timeoutForMediumTests = $timeout; + } + + /** + * Sets the timeout for large tests. + * + * @param int $timeout + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.6.0 + */ + public function setTimeoutForLargeTests($timeout) + { + if (!is_integer($timeout)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->timeoutForLargeTests = $timeout; + } + + /** + * Returns the class hierarchy for a given class. + * + * @param string $className + * @param bool $asReflectionObjects + * + * @return array + */ + protected function getHierarchy($className, $asReflectionObjects = false) + { + if ($asReflectionObjects) { + $classes = array(new ReflectionClass($className)); + } else { + $classes = array($className); + } + + $done = false; + + while (!$done) { + if ($asReflectionObjects) { + $class = new ReflectionClass( + $classes[count($classes) - 1]->getName() + ); + } else { + $class = new ReflectionClass($classes[count($classes) - 1]); + } + + $parent = $class->getParentClass(); + + if ($parent !== false) { + if ($asReflectionObjects) { + $classes[] = $parent; + } else { + $classes[] = $parent->getName(); + } + } else { + $done = true; + } + } + + return $classes; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Thrown when an assertion failed. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_AssertionFailedError extends PHPUnit_Framework_Exception implements PHPUnit_Framework_SelfDescribing +{ + /** + * Wrapper for getMessage() which is declared as final. + * + * @return string + */ + public function toString() + { + return $this->getMessage(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestSuite is a composite of Tests. It runs a collection of test cases. + * + * Here is an example using the dynamic test definition. + * + * + * addTest(new MathTest('testPass')); + * ?> + * + * + * Alternatively, a TestSuite can extract the tests to be run automatically. + * To do so you pass a ReflectionClass instance for your + * PHPUnit_Framework_TestCase class to the PHPUnit_Framework_TestSuite + * constructor. + * + * + * + * + * + * This constructor creates a suite with all the methods starting with + * "test" that take no arguments. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_TestSuite implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing, IteratorAggregate +{ + /** + * Last count of tests in this suite. + * + * @var int|null + */ + private $cachedNumTests; + + /** + * Enable or disable the backup and restoration of the $GLOBALS array. + * + * @var bool + */ + protected $backupGlobals = null; + + /** + * Enable or disable the backup and restoration of static attributes. + * + * @var bool + */ + protected $backupStaticAttributes = null; + + /** + * @var bool + */ + private $disallowChangesToGlobalState = null; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * The name of the test suite. + * + * @var string + */ + protected $name = ''; + + /** + * The test groups of the test suite. + * + * @var array + */ + protected $groups = array(); + + /** + * The tests in the test suite. + * + * @var array + */ + protected $tests = array(); + + /** + * The number of tests in the test suite. + * + * @var int + */ + protected $numTests = -1; + + /** + * @var bool + */ + protected $testCase = false; + + /** + * @var array + */ + protected $foundClasses = array(); + + /** + * @var PHPUnit_Runner_Filter_Factory + */ + private $iteratorFilter = null; + + /** + * Constructs a new TestSuite: + * + * - PHPUnit_Framework_TestSuite() constructs an empty TestSuite. + * + * - PHPUnit_Framework_TestSuite(ReflectionClass) constructs a + * TestSuite from the given class. + * + * - PHPUnit_Framework_TestSuite(ReflectionClass, String) + * constructs a TestSuite from the given class with the given + * name. + * + * - PHPUnit_Framework_TestSuite(String) either constructs a + * TestSuite from the given class (if the passed string is the + * name of an existing class) or constructs an empty TestSuite + * with the given name. + * + * @param mixed $theClass + * @param string $name + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($theClass = '', $name = '') + { + $argumentsValid = false; + + if (is_object($theClass) && + $theClass instanceof ReflectionClass) { + $argumentsValid = true; + } elseif (is_string($theClass) && + $theClass !== '' && + class_exists($theClass, false)) { + $argumentsValid = true; + + if ($name == '') { + $name = $theClass; + } + + $theClass = new ReflectionClass($theClass); + } elseif (is_string($theClass)) { + $this->setName($theClass); + + return; + } + + if (!$argumentsValid) { + throw new PHPUnit_Framework_Exception; + } + + if (!$theClass->isSubclassOf('PHPUnit_Framework_TestCase')) { + throw new PHPUnit_Framework_Exception( + 'Class "' . $theClass->name . '" does not extend PHPUnit_Framework_TestCase.' + ); + } + + if ($name != '') { + $this->setName($name); + } else { + $this->setName($theClass->getName()); + } + + $constructor = $theClass->getConstructor(); + + if ($constructor !== null && + !$constructor->isPublic()) { + $this->addTest( + self::warning( + sprintf( + 'Class "%s" has no public constructor.', + $theClass->getName() + ) + ) + ); + + return; + } + + foreach ($theClass->getMethods() as $method) { + $this->addTestMethod($theClass, $method); + } + + if (empty($this->tests)) { + $this->addTest( + self::warning( + sprintf( + 'No tests found in class "%s".', + $theClass->getName() + ) + ) + ); + } + + $this->testCase = true; + } + + /** + * Returns a string representation of the test suite. + * + * @return string + */ + public function toString() + { + return $this->getName(); + } + + /** + * Adds a test to the suite. + * + * @param PHPUnit_Framework_Test $test + * @param array $groups + */ + public function addTest(PHPUnit_Framework_Test $test, $groups = array()) + { + $class = new ReflectionClass($test); + + if (!$class->isAbstract()) { + $this->tests[] = $test; + $this->numTests = -1; + + if ($test instanceof self && + empty($groups)) { + $groups = $test->getGroups(); + } + + if (empty($groups)) { + $groups = array('default'); + } + + foreach ($groups as $group) { + if (!isset($this->groups[$group])) { + $this->groups[$group] = array($test); + } else { + $this->groups[$group][] = $test; + } + } + } + } + + /** + * Adds the tests from the given class to the suite. + * + * @param mixed $testClass + * + * @throws PHPUnit_Framework_Exception + */ + public function addTestSuite($testClass) + { + if (is_string($testClass) && class_exists($testClass)) { + $testClass = new ReflectionClass($testClass); + } + + if (!is_object($testClass)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'class name or object' + ); + } + + if ($testClass instanceof self) { + $this->addTest($testClass); + } elseif ($testClass instanceof ReflectionClass) { + $suiteMethod = false; + + if (!$testClass->isAbstract()) { + if ($testClass->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) { + $method = $testClass->getMethod( + PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME + ); + + if ($method->isStatic()) { + $this->addTest( + $method->invoke(null, $testClass->getName()) + ); + + $suiteMethod = true; + } + } + } + + if (!$suiteMethod && !$testClass->isAbstract()) { + $this->addTest(new self($testClass)); + } + } else { + throw new PHPUnit_Framework_Exception; + } + } + + /** + * Wraps both addTest() and addTestSuite + * as well as the separate import statements for the user's convenience. + * + * If the named file cannot be read or there are no new tests that can be + * added, a PHPUnit_Framework_Warning will be created instead, + * leaving the current test run untouched. + * + * @param string $filename + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 2.3.0 + */ + public function addTestFile($filename) + { + if (!is_string($filename)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + if (file_exists($filename) && substr($filename, -5) == '.phpt') { + $this->addTest( + new PHPUnit_Extensions_PhptTestCase($filename) + ); + + return; + } + + // The given file may contain further stub classes in addition to the + // test class itself. Figure out the actual test class. + $classes = get_declared_classes(); + $filename = PHPUnit_Util_Fileloader::checkAndLoad($filename); + $newClasses = array_diff(get_declared_classes(), $classes); + + // The diff is empty in case a parent class (with test methods) is added + // AFTER a child class that inherited from it. To account for that case, + // cumulate all discovered classes, so the parent class may be found in + // a later invocation. + if ($newClasses) { + // On the assumption that test classes are defined first in files, + // process discovered classes in approximate LIFO order, so as to + // avoid unnecessary reflection. + $this->foundClasses = array_merge($newClasses, $this->foundClasses); + } + + // The test class's name must match the filename, either in full, or as + // a PEAR/PSR-0 prefixed shortname ('NameSpace_ShortName'), or as a + // PSR-1 local shortname ('NameSpace\ShortName'). The comparison must be + // anchored to prevent false-positive matches (e.g., 'OtherShortName'). + $shortname = basename($filename, '.php'); + $shortnameRegEx = '/(?:^|_|\\\\)' . preg_quote($shortname, '/') . '$/'; + + foreach ($this->foundClasses as $i => $className) { + if (preg_match($shortnameRegEx, $className)) { + $class = new ReflectionClass($className); + + if ($class->getFileName() == $filename) { + $newClasses = array($className); + unset($this->foundClasses[$i]); + break; + } + } + } + + foreach ($newClasses as $className) { + $class = new ReflectionClass($className); + + if (!$class->isAbstract()) { + if ($class->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) { + $method = $class->getMethod( + PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME + ); + + if ($method->isStatic()) { + $this->addTest($method->invoke(null, $className)); + } + } elseif ($class->implementsInterface('PHPUnit_Framework_Test')) { + $this->addTestSuite($class); + } + } + } + + $this->numTests = -1; + } + + /** + * Wrapper for addTestFile() that adds multiple test files. + * + * @param array|Iterator $filenames + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 2.3.0 + */ + public function addTestFiles($filenames) + { + if (!(is_array($filenames) || + (is_object($filenames) && $filenames instanceof Iterator))) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'array or iterator' + ); + } + + foreach ($filenames as $filename) { + $this->addTestFile((string) $filename); + } + } + + /** + * Counts the number of test cases that will be run by this test. + * + * @param bool $preferCache Indicates if cache is preferred. + * + * @return int + */ + public function count($preferCache = false) + { + if ($preferCache && $this->cachedNumTests != null) { + $numTests = $this->cachedNumTests; + } else { + $numTests = 0; + foreach ($this as $test) { + $numTests += count($test); + } + $this->cachedNumTests = $numTests; + } + + return $numTests; + } + + /** + * @param ReflectionClass $theClass + * @param string $name + * + * @return PHPUnit_Framework_Test + * + * @throws PHPUnit_Framework_Exception + */ + public static function createTest(ReflectionClass $theClass, $name) + { + $className = $theClass->getName(); + + if (!$theClass->isInstantiable()) { + return self::warning( + sprintf('Cannot instantiate class "%s".', $className) + ); + } + + $backupSettings = PHPUnit_Util_Test::getBackupSettings( + $className, + $name + ); + + $preserveGlobalState = PHPUnit_Util_Test::getPreserveGlobalStateSettings( + $className, + $name + ); + + $runTestInSeparateProcess = PHPUnit_Util_Test::getProcessIsolationSettings( + $className, + $name + ); + + $constructor = $theClass->getConstructor(); + + if ($constructor !== null) { + $parameters = $constructor->getParameters(); + + // TestCase() or TestCase($name) + if (count($parameters) < 2) { + $test = new $className; + } // TestCase($name, $data) + else { + try { + $data = PHPUnit_Util_Test::getProvidedData( + $className, + $name + ); + } catch (PHPUnit_Framework_IncompleteTestError $e) { + $message = sprintf( + 'Test for %s::%s marked incomplete by data provider', + $className, + $name + ); + + $_message = $e->getMessage(); + + if (!empty($_message)) { + $message .= "\n" . $_message; + } + + $data = self::incompleteTest($className, $name, $message); + } catch (PHPUnit_Framework_SkippedTestError $e) { + $message = sprintf( + 'Test for %s::%s skipped by data provider', + $className, + $name + ); + + $_message = $e->getMessage(); + + if (!empty($_message)) { + $message .= "\n" . $_message; + } + + $data = self::skipTest($className, $name, $message); + } catch (Throwable $_t) { + $t = $_t; + } catch (Exception $_t) { + $t = $_t; + } + + if (isset($t)) { + $message = sprintf( + 'The data provider specified for %s::%s is invalid.', + $className, + $name + ); + + $_message = $t->getMessage(); + + if (!empty($_message)) { + $message .= "\n" . $_message; + } + + $data = self::warning($message); + } + + // Test method with @dataProvider. + if (isset($data)) { + $test = new PHPUnit_Framework_TestSuite_DataProvider( + $className . '::' . $name + ); + + if (empty($data)) { + $data = self::warning( + sprintf( + 'No tests found in suite "%s".', + $test->getName() + ) + ); + } + + $groups = PHPUnit_Util_Test::getGroups($className, $name); + + if ($data instanceof PHPUnit_Framework_Warning || + $data instanceof PHPUnit_Framework_SkippedTestCase || + $data instanceof PHPUnit_Framework_IncompleteTestCase) { + $test->addTest($data, $groups); + } else { + foreach ($data as $_dataName => $_data) { + $_test = new $className($name, $_data, $_dataName); + + if ($runTestInSeparateProcess) { + $_test->setRunTestInSeparateProcess(true); + + if ($preserveGlobalState !== null) { + $_test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($backupSettings['backupGlobals'] !== null) { + $_test->setBackupGlobals( + $backupSettings['backupGlobals'] + ); + } + + if ($backupSettings['backupStaticAttributes'] !== null) { + $_test->setBackupStaticAttributes( + $backupSettings['backupStaticAttributes'] + ); + } + + $test->addTest($_test, $groups); + } + } + } else { + $test = new $className; + } + } + } + + if (!isset($test)) { + throw new PHPUnit_Framework_Exception('No valid test provided.'); + } + + if ($test instanceof PHPUnit_Framework_TestCase) { + $test->setName($name); + + if ($runTestInSeparateProcess) { + $test->setRunTestInSeparateProcess(true); + + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($backupSettings['backupGlobals'] !== null) { + $test->setBackupGlobals($backupSettings['backupGlobals']); + } + + if ($backupSettings['backupStaticAttributes'] !== null) { + $test->setBackupStaticAttributes( + $backupSettings['backupStaticAttributes'] + ); + } + } + + return $test; + } + + /** + * Creates a default TestResult object. + * + * @return PHPUnit_Framework_TestResult + */ + protected function createResult() + { + return new PHPUnit_Framework_TestResult; + } + + /** + * Returns the name of the suite. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the test groups of the suite. + * + * @return array + * + * @since Method available since Release 3.2.0 + */ + public function getGroups() + { + return array_keys($this->groups); + } + + public function getGroupDetails() + { + return $this->groups; + } + + /** + * Set tests groups of the test case + * + * @param array $groups + * + * @since Method available since Release 4.0.0 + */ + public function setGroupDetails(array $groups) + { + $this->groups = $groups; + } + + /** + * Runs the tests and collects their result in a TestResult. + * + * @param PHPUnit_Framework_TestResult $result + * + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = null) + { + if ($result === null) { + $result = $this->createResult(); + } + + if (count($this) == 0) { + return $result; + } + + $hookMethods = PHPUnit_Util_Test::getHookMethods($this->name); + + $result->startTestSuite($this); + + try { + $this->setUp(); + + foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { + if ($this->testCase === true && + class_exists($this->name, false) && + method_exists($this->name, $beforeClassMethod)) { + if ($missingRequirements = PHPUnit_Util_Test::getMissingRequirements($this->name, $beforeClassMethod)) { + $this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements)); + } + + call_user_func(array($this->name, $beforeClassMethod)); + } + } + } catch (PHPUnit_Framework_SkippedTestSuiteError $e) { + $numTests = count($this); + + for ($i = 0; $i < $numTests; $i++) { + $result->startTest($this); + $result->addFailure($this, $e, 0); + $result->endTest($this, 0); + } + + $this->tearDown(); + $result->endTestSuite($this); + + return $result; + } catch (Throwable $_t) { + $t = $_t; + } catch (Exception $_t) { + $t = $_t; + } + + if (isset($t)) { + $numTests = count($this); + + for ($i = 0; $i < $numTests; $i++) { + $result->startTest($this); + $result->addError($this, $t, 0); + $result->endTest($this, 0); + } + + $this->tearDown(); + $result->endTestSuite($this); + + return $result; + } + + foreach ($this as $test) { + if ($result->shouldStop()) { + break; + } + + if ($test instanceof PHPUnit_Framework_TestCase || + $test instanceof self) { + $test->setDisallowChangesToGlobalState($this->disallowChangesToGlobalState); + $test->setBackupGlobals($this->backupGlobals); + $test->setBackupStaticAttributes($this->backupStaticAttributes); + $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); + } + + $test->run($result); + } + + foreach ($hookMethods['afterClass'] as $afterClassMethod) { + if ($this->testCase === true && class_exists($this->name, false) && method_exists($this->name, $afterClassMethod)) { + call_user_func(array($this->name, $afterClassMethod)); + } + } + + $this->tearDown(); + + $result->endTestSuite($this); + + return $result; + } + + /** + * @param bool $runTestInSeparateProcess + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.7.0 + */ + public function setRunTestInSeparateProcess($runTestInSeparateProcess) + { + if (is_bool($runTestInSeparateProcess)) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * Runs a test. + * + * @deprecated + * + * @param PHPUnit_Framework_Test $test + * @param PHPUnit_Framework_TestResult $result + */ + public function runTest(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result) + { + $test->run($result); + } + + /** + * Sets the name of the suite. + * + * @param string + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Returns the test at the given index. + * + * @param int + * + * @return PHPUnit_Framework_Test + */ + public function testAt($index) + { + if (isset($this->tests[$index])) { + return $this->tests[$index]; + } else { + return false; + } + } + + /** + * Returns the tests as an enumeration. + * + * @return array + */ + public function tests() + { + return $this->tests; + } + + /** + * Set tests of the test suite + * + * @param array $tests + * + * @since Method available since Release 4.0.0 + */ + public function setTests(array $tests) + { + $this->tests = $tests; + } + + /** + * Mark the test suite as skipped. + * + * @param string $message + * + * @throws PHPUnit_Framework_SkippedTestSuiteError + * + * @since Method available since Release 3.0.0 + */ + public function markTestSuiteSkipped($message = '') + { + throw new PHPUnit_Framework_SkippedTestSuiteError($message); + } + + /** + * @param ReflectionClass $class + * @param ReflectionMethod $method + */ + protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method) + { + if (!$this->isTestMethod($method)) { + return; + } + + $name = $method->getName(); + + if (!$method->isPublic()) { + $this->addTest( + self::warning( + sprintf( + 'Test method "%s" in test class "%s" is not public.', + $name, + $class->getName() + ) + ) + ); + + return; + } + + $test = self::createTest($class, $name); + + if ($test instanceof PHPUnit_Framework_TestCase || + $test instanceof PHPUnit_Framework_TestSuite_DataProvider) { + $test->setDependencies( + PHPUnit_Util_Test::getDependencies($class->getName(), $name) + ); + } + + $this->addTest( + $test, + PHPUnit_Util_Test::getGroups($class->getName(), $name) + ); + } + + /** + * @param ReflectionMethod $method + * + * @return bool + */ + public static function isTestMethod(ReflectionMethod $method) + { + if (strpos($method->name, 'test') === 0) { + return true; + } + + // @scenario on TestCase::testMethod() + // @test on TestCase::testMethod() + $doc_comment = $method->getDocComment(); + + return strpos($doc_comment, '@test') !== false || + strpos($doc_comment, '@scenario') !== false; + } + + /** + * @param string $message + * + * @return PHPUnit_Framework_Warning + */ + protected static function warning($message) + { + return new PHPUnit_Framework_Warning($message); + } + + /** + * @param string $class + * @param string $methodName + * @param string $message + * + * @return PHPUnit_Framework_SkippedTestCase + * + * @since Method available since Release 4.3.0 + */ + protected static function skipTest($class, $methodName, $message) + { + return new PHPUnit_Framework_SkippedTestCase($class, $methodName, $message); + } + + /** + * @param string $class + * @param string $methodName + * @param string $message + * + * @return PHPUnit_Framework_IncompleteTestCase + * + * @since Method available since Release 4.3.0 + */ + protected static function incompleteTest($class, $methodName, $message) + { + return new PHPUnit_Framework_IncompleteTestCase($class, $methodName, $message); + } + + /** + * @param bool $disallowChangesToGlobalState + * + * @since Method available since Release 4.6.0 + */ + public function setDisallowChangesToGlobalState($disallowChangesToGlobalState) + { + if (is_null($this->disallowChangesToGlobalState) && is_bool($disallowChangesToGlobalState)) { + $this->disallowChangesToGlobalState = $disallowChangesToGlobalState; + } + } + + /** + * @param bool $backupGlobals + * + * @since Method available since Release 3.3.0 + */ + public function setBackupGlobals($backupGlobals) + { + if (is_null($this->backupGlobals) && is_bool($backupGlobals)) { + $this->backupGlobals = $backupGlobals; + } + } + + /** + * @param bool $backupStaticAttributes + * + * @since Method available since Release 3.4.0 + */ + public function setBackupStaticAttributes($backupStaticAttributes) + { + if (is_null($this->backupStaticAttributes) && + is_bool($backupStaticAttributes)) { + $this->backupStaticAttributes = $backupStaticAttributes; + } + } + + /** + * Returns an iterator for this test suite. + * + * @return RecursiveIteratorIterator + * + * @since Method available since Release 3.1.0 + */ + public function getIterator() + { + $iterator = new PHPUnit_Util_TestSuiteIterator($this); + + if ($this->iteratorFilter !== null) { + $iterator = $this->iteratorFilter->factory($iterator, $this); + } + + return $iterator; + } + + public function injectFilter(PHPUnit_Runner_Filter_Factory $filter) + { + $this->iteratorFilter = $filter; + foreach ($this as $test) { + if ($test instanceof self) { + $test->injectFilter($filter); + } + } + } + + /** + * Template Method that is called before the tests + * of this test suite are run. + * + * @since Method available since Release 3.1.0 + */ + protected function setUp() + { + } + + /** + * Template Method that is called after the tests + * of this test suite have finished running. + * + * @since Method available since Release 3.1.0 + */ + protected function tearDown() + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An empty Listener that can be extended to implement TestListener + * with just a few lines of code. + * + * @see PHPUnit_Framework_TestListener for documentation on the API methods. + * @since Class available since Release 4.0.0 + */ +abstract class PHPUnit_Framework_BaseTestListener implements PHPUnit_Framework_TestListener +{ + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + } + + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) + { + } + + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) + { + } + + public function startTest(PHPUnit_Framework_Test $test) + { + } + + public function endTest(PHPUnit_Framework_Test $test, $time) + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a risky test. + * + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Framework_RiskyTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_RiskyTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a test that is skipped because of an invalid @covers annotation. + * + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Framework_InvalidCoversTargetError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SebastianBergmann\GlobalState\Snapshot; +use SebastianBergmann\GlobalState\Restorer; +use SebastianBergmann\GlobalState\Blacklist; +use SebastianBergmann\Diff\Differ; +use SebastianBergmann\Exporter\Exporter; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Prophet; + +/** + * A TestCase defines the fixture to run multiple tests. + * + * To define a TestCase + * + * 1) Implement a subclass of PHPUnit_Framework_TestCase. + * 2) Define instance variables that store the state of the fixture. + * 3) Initialize the fixture state by overriding setUp(). + * 4) Clean-up after a test by overriding tearDown(). + * + * Each test runs in its own fixture so there can be no side effects + * among test runs. + * + * Here is an example: + * + * + * value1 = 2; + * $this->value2 = 3; + * } + * } + * ?> + * + * + * For each test implement a method which interacts with the fixture. + * Verify the expected results with assertions specified by calling + * assert with a boolean. + * + * + * assertTrue($this->value1 + $this->value2 == 5); + * } + * ?> + * + * + * @since Class available since Release 2.0.0 + */ +abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing +{ + /** + * Enable or disable the backup and restoration of the $GLOBALS array. + * Overwrite this attribute in a child class of TestCase. + * Setting this attribute in setUp() has no effect! + * + * @var bool + */ + protected $backupGlobals = null; + + /** + * @var array + */ + protected $backupGlobalsBlacklist = array(); + + /** + * Enable or disable the backup and restoration of static attributes. + * Overwrite this attribute in a child class of TestCase. + * Setting this attribute in setUp() has no effect! + * + * @var bool + */ + protected $backupStaticAttributes = null; + + /** + * @var array + */ + protected $backupStaticAttributesBlacklist = array(); + + /** + * Whether or not this test is to be run in a separate PHP process. + * + * @var bool + */ + protected $runTestInSeparateProcess = null; + + /** + * Whether or not this test should preserve the global state when + * running in a separate PHP process. + * + * @var bool + */ + protected $preserveGlobalState = true; + + /** + * Whether or not this test is running in a separate PHP process. + * + * @var bool + */ + private $inIsolation = false; + + /** + * @var array + */ + private $data = array(); + + /** + * @var string + */ + private $dataName = ''; + + /** + * @var bool + */ + private $useErrorHandler = null; + + /** + * The name of the expected Exception. + * + * @var mixed + */ + private $expectedException = null; + + /** + * The message of the expected Exception. + * + * @var string + */ + private $expectedExceptionMessage = ''; + + /** + * The regex pattern to validate the expected Exception message. + * + * @var string + */ + private $expectedExceptionMessageRegExp = ''; + + /** + * The code of the expected Exception. + * + * @var int + */ + private $expectedExceptionCode; + + /** + * The name of the test case. + * + * @var string + */ + private $name = null; + + /** + * @var array + */ + private $dependencies = array(); + + /** + * @var array + */ + private $dependencyInput = array(); + + /** + * @var array + */ + private $iniSettings = array(); + + /** + * @var array + */ + private $locale = array(); + + /** + * @var array + */ + private $mockObjects = array(); + + /** + * @var array + */ + private $mockObjectGenerator = null; + + /** + * @var int + */ + private $status; + + /** + * @var string + */ + private $statusMessage = ''; + + /** + * @var int + */ + private $numAssertions = 0; + + /** + * @var PHPUnit_Framework_TestResult + */ + private $result; + + /** + * @var mixed + */ + private $testResult; + + /** + * @var string + */ + private $output = ''; + + /** + * @var string + */ + private $outputExpectedRegex = null; + + /** + * @var string + */ + private $outputExpectedString = null; + + /** + * @var mixed + */ + private $outputCallback = false; + + /** + * @var bool + */ + private $outputBufferingActive = false; + + /** + * @var int + */ + private $outputBufferingLevel; + + /** + * @var SebastianBergmann\GlobalState\Snapshot + */ + private $snapshot; + + /** + * @var Prophecy\Prophet + */ + private $prophet; + + /** + * @var bool + */ + private $disallowChangesToGlobalState = false; + + /** + * Constructs a test case with the given name. + * + * @param string $name + * @param array $data + * @param string $dataName + */ + public function __construct($name = null, array $data = array(), $dataName = '') + { + if ($name !== null) { + $this->setName($name); + } + + $this->data = $data; + $this->dataName = $dataName; + } + + /** + * Returns a string representation of the test case. + * + * @return string + */ + public function toString() + { + $class = new ReflectionClass($this); + + $buffer = sprintf( + '%s::%s', + $class->name, + $this->getName(false) + ); + + return $buffer . $this->getDataSetAsString(); + } + + /** + * Counts the number of test cases executed by run(TestResult result). + * + * @return int + */ + public function count() + { + return 1; + } + + /** + * Returns the annotations for this test. + * + * @return array + * + * @since Method available since Release 3.4.0 + */ + public function getAnnotations() + { + return PHPUnit_Util_Test::parseTestMethodAnnotations( + get_class($this), + $this->name + ); + } + + /** + * Gets the name of a TestCase. + * + * @param bool $withDataSet + * + * @return string + */ + public function getName($withDataSet = true) + { + if ($withDataSet) { + return $this->name . $this->getDataSetAsString(false); + } else { + return $this->name; + } + } + + /** + * Returns the size of the test. + * + * @return int + * + * @since Method available since Release 3.6.0 + */ + public function getSize() + { + return PHPUnit_Util_Test::getSize( + get_class($this), + $this->getName(false) + ); + } + + /** + * @return string + * + * @since Method available since Release 3.6.0 + */ + public function getActualOutput() + { + if (!$this->outputBufferingActive) { + return $this->output; + } else { + return ob_get_contents(); + } + } + + /** + * @return bool + * + * @since Method available since Release 3.6.0 + */ + public function hasOutput() + { + if (strlen($this->output) === 0) { + return false; + } + + if ($this->hasExpectationOnOutput()) { + return false; + } + + return true; + } + + /** + * @param string $expectedRegex + * + * @since Method available since Release 3.6.0 + * + * @throws PHPUnit_Framework_Exception + */ + public function expectOutputRegex($expectedRegex) + { + if ($this->outputExpectedString !== null) { + throw new PHPUnit_Framework_Exception; + } + + if (is_string($expectedRegex) || is_null($expectedRegex)) { + $this->outputExpectedRegex = $expectedRegex; + } + } + + /** + * @param string $expectedString + * + * @since Method available since Release 3.6.0 + */ + public function expectOutputString($expectedString) + { + if ($this->outputExpectedRegex !== null) { + throw new PHPUnit_Framework_Exception; + } + + if (is_string($expectedString) || is_null($expectedString)) { + $this->outputExpectedString = $expectedString; + } + } + + /** + * @return bool + * + * @since Method available since Release 3.6.5 + * @deprecated + */ + public function hasPerformedExpectationsOnOutput() + { + return $this->hasExpectationOnOutput(); + } + + /** + * @return bool + * + * @since Method available since Release 4.3.3 + */ + public function hasExpectationOnOutput() + { + return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex); + } + + /** + * @return string + * + * @since Method available since Release 3.2.0 + */ + public function getExpectedException() + { + return $this->expectedException; + } + + /** + * @param mixed $exceptionName + * @param string $exceptionMessage + * @param int $exceptionCode + * + * @since Method available since Release 3.2.0 + */ + public function setExpectedException($exceptionName, $exceptionMessage = '', $exceptionCode = null) + { + $this->expectedException = $exceptionName; + $this->expectedExceptionMessage = $exceptionMessage; + $this->expectedExceptionCode = $exceptionCode; + } + + /** + * @param mixed $exceptionName + * @param string $exceptionMessageRegExp + * @param int $exceptionCode + * + * @since Method available since Release 4.3.0 + */ + public function setExpectedExceptionRegExp($exceptionName, $exceptionMessageRegExp = '', $exceptionCode = null) + { + $this->expectedException = $exceptionName; + $this->expectedExceptionMessageRegExp = $exceptionMessageRegExp; + $this->expectedExceptionCode = $exceptionCode; + } + + /** + * @since Method available since Release 3.4.0 + */ + protected function setExpectedExceptionFromAnnotation() + { + try { + $expectedException = PHPUnit_Util_Test::getExpectedException( + get_class($this), + $this->name + ); + + if ($expectedException !== false) { + $this->setExpectedException( + $expectedException['class'], + $expectedException['message'], + $expectedException['code'] + ); + + if (!empty($expectedException['message_regex'])) { + $this->setExpectedExceptionRegExp( + $expectedException['class'], + $expectedException['message_regex'], + $expectedException['code'] + ); + } + } + } catch (ReflectionException $e) { + } + } + + /** + * @param bool $useErrorHandler + * + * @since Method available since Release 3.4.0 + */ + public function setUseErrorHandler($useErrorHandler) + { + $this->useErrorHandler = $useErrorHandler; + } + + /** + * @since Method available since Release 3.4.0 + */ + protected function setUseErrorHandlerFromAnnotation() + { + try { + $useErrorHandler = PHPUnit_Util_Test::getErrorHandlerSettings( + get_class($this), + $this->name + ); + + if ($useErrorHandler !== null) { + $this->setUseErrorHandler($useErrorHandler); + } + } catch (ReflectionException $e) { + } + } + + /** + * @since Method available since Release 3.6.0 + */ + protected function checkRequirements() + { + if (!$this->name || !method_exists($this, $this->name)) { + return; + } + + $missingRequirements = PHPUnit_Util_Test::getMissingRequirements( + get_class($this), + $this->name + ); + + if (!empty($missingRequirements)) { + $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); + } + } + + /** + * Returns the status of this test. + * + * @return int + * + * @since Method available since Release 3.1.0 + */ + public function getStatus() + { + return $this->status; + } + + /** + * Returns the status message of this test. + * + * @return string + * + * @since Method available since Release 3.3.0 + */ + public function getStatusMessage() + { + return $this->statusMessage; + } + + /** + * Returns whether or not this test has failed. + * + * @return bool + * + * @since Method available since Release 3.0.0 + */ + public function hasFailed() + { + $status = $this->getStatus(); + + return $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE || + $status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; + } + + /** + * Runs the test case and collects the results in a TestResult object. + * If no TestResult object is passed a new one will be created. + * + * @param PHPUnit_Framework_TestResult $result + * + * @return PHPUnit_Framework_TestResult + * + * @throws PHPUnit_Framework_Exception + */ + public function run(PHPUnit_Framework_TestResult $result = null) + { + if ($result === null) { + $result = $this->createResult(); + } + + if (!$this instanceof PHPUnit_Framework_Warning) { + $this->setTestResultObject($result); + $this->setUseErrorHandlerFromAnnotation(); + } + + if ($this->useErrorHandler !== null) { + $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions(); + $result->convertErrorsToExceptions($this->useErrorHandler); + } + + if (!$this instanceof PHPUnit_Framework_Warning && !$this->handleDependencies()) { + return; + } + + if ($this->runTestInSeparateProcess === true && + $this->inIsolation !== true && + !$this instanceof PHPUnit_Extensions_SeleniumTestCase && + !$this instanceof PHPUnit_Extensions_PhptTestCase) { + $class = new ReflectionClass($this); + + $template = new Text_Template( + __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl' + ); + + if ($this->preserveGlobalState) { + $constants = PHPUnit_Util_GlobalState::getConstantsAsString(); + $globals = PHPUnit_Util_GlobalState::getGlobalsAsString(); + $includedFiles = PHPUnit_Util_GlobalState::getIncludedFilesAsString(); + $iniSettings = PHPUnit_Util_GlobalState::getIniSettingsAsString(); + } else { + $constants = ''; + if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n"; + } else { + $globals = ''; + } + $includedFiles = ''; + $iniSettings = ''; + } + + $coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false'; + $isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false'; + $isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false'; + $isStrictAboutTestSize = $result->isStrictAboutTestSize() ? 'true' : 'false'; + $isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false'; + + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true); + } else { + $composerAutoload = '\'\''; + } + + if (defined('__PHPUNIT_PHAR__')) { + $phar = var_export(__PHPUNIT_PHAR__, true); + } else { + $phar = '\'\''; + } + + if ($result->getCodeCoverage()) { + $codeCoverageFilter = $result->getCodeCoverage()->filter(); + } else { + $codeCoverageFilter = null; + } + + $data = var_export(serialize($this->data), true); + $dataName = var_export($this->dataName, true); + $dependencyInput = var_export(serialize($this->dependencyInput), true); + $includePath = var_export(get_include_path(), true); + $codeCoverageFilter = var_export(serialize($codeCoverageFilter), true); + // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC + // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences + $data = "'." . $data . ".'"; + $dataName = "'.(" . $dataName . ").'"; + $dependencyInput = "'." . $dependencyInput . ".'"; + $includePath = "'." . $includePath . ".'"; + $codeCoverageFilter = "'." . $codeCoverageFilter . ".'"; + + $configurationFilePath = (isset($GLOBALS['__PHPUNIT_CONFIGURATION_FILE']) ? $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] : ''); + + $template->setVar( + array( + 'composerAutoload' => $composerAutoload, + 'phar' => $phar, + 'filename' => $class->getFileName(), + 'className' => $class->getName(), + 'methodName' => $this->name, + 'collectCodeCoverageInformation' => $coverage, + 'data' => $data, + 'dataName' => $dataName, + 'dependencyInput' => $dependencyInput, + 'constants' => $constants, + 'globals' => $globals, + 'include_path' => $includePath, + 'included_files' => $includedFiles, + 'iniSettings' => $iniSettings, + 'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything, + 'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests, + 'isStrictAboutTestSize' => $isStrictAboutTestSize, + 'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests, + 'codeCoverageFilter' => $codeCoverageFilter, + 'configurationFilePath' => $configurationFilePath + ) + ); + + $this->prepareTemplate($template); + + $php = PHPUnit_Util_PHP::factory(); + $php->runTestJob($template->render(), $this, $result); + } else { + $result->run($this); + } + + if ($this->useErrorHandler !== null) { + $result->convertErrorsToExceptions($oldErrorHandlerSetting); + } + + $this->result = null; + + return $result; + } + + /** + * Runs the bare test sequence. + */ + public function runBare() + { + $this->numAssertions = 0; + + $this->snapshotGlobalState(); + $this->startOutputBuffering(); + clearstatcache(); + $currentWorkingDirectory = getcwd(); + + $hookMethods = PHPUnit_Util_Test::getHookMethods(get_class($this)); + + try { + $hasMetRequirements = false; + $this->checkRequirements(); + $hasMetRequirements = true; + + if ($this->inIsolation) { + foreach ($hookMethods['beforeClass'] as $method) { + $this->$method(); + } + } + + $this->setExpectedExceptionFromAnnotation(); + + foreach ($hookMethods['before'] as $method) { + $this->$method(); + } + + $this->assertPreConditions(); + $this->testResult = $this->runTest(); + $this->verifyMockObjects(); + $this->assertPostConditions(); + + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED; + } catch (PHPUnit_Framework_IncompleteTest $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE; + $this->statusMessage = $e->getMessage(); + } catch (PHPUnit_Framework_SkippedTest $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED; + $this->statusMessage = $e->getMessage(); + } catch (PHPUnit_Framework_AssertionFailedError $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; + $this->statusMessage = $e->getMessage(); + } catch (PredictionException $e) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; + $this->statusMessage = $e->getMessage(); + } catch (Throwable $_e) { + $e = $_e; + } catch (Exception $_e) { + $e = $_e; + } + + if (isset($_e)) { + $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; + $this->statusMessage = $_e->getMessage(); + } + + // Clean up the mock objects. + $this->mockObjects = array(); + $this->prophet = null; + + // Tear down the fixture. An exception raised in tearDown() will be + // caught and passed on when no exception was raised before. + try { + if ($hasMetRequirements) { + foreach ($hookMethods['after'] as $method) { + $this->$method(); + } + + if ($this->inIsolation) { + foreach ($hookMethods['afterClass'] as $method) { + $this->$method(); + } + } + } + } catch (Throwable $_e) { + if (!isset($e)) { + $e = $_e; + } + } catch (Exception $_e) { + if (!isset($e)) { + $e = $_e; + } + } + + try { + $this->stopOutputBuffering(); + } catch (PHPUnit_Framework_RiskyTestError $_e) { + if (!isset($e)) { + $e = $_e; + } + } + + clearstatcache(); + + if ($currentWorkingDirectory != getcwd()) { + chdir($currentWorkingDirectory); + } + + $this->restoreGlobalState(); + + // Clean up INI settings. + foreach ($this->iniSettings as $varName => $oldValue) { + ini_set($varName, $oldValue); + } + + $this->iniSettings = array(); + + // Clean up locale settings. + foreach ($this->locale as $category => $locale) { + setlocale($category, $locale); + } + + // Perform assertion on output. + if (!isset($e)) { + try { + if ($this->outputExpectedRegex !== null) { + $this->assertRegExp($this->outputExpectedRegex, $this->output); + } elseif ($this->outputExpectedString !== null) { + $this->assertEquals($this->outputExpectedString, $this->output); + } + } catch (Throwable $_e) { + $e = $_e; + } catch (Exception $_e) { + $e = $_e; + } + } + + // Workaround for missing "finally". + if (isset($e)) { + if ($e instanceof PredictionException) { + $e = new PHPUnit_Framework_AssertionFailedError($e->getMessage()); + } + + if (!$e instanceof Exception) { + // Rethrow Error directly on PHP 7 as onNotSuccessfulTest does not support it + throw $e; + } + + $this->onNotSuccessfulTest($e); + } + } + + /** + * Override to run the test and assert its state. + * + * @return mixed + * + * @throws Exception|PHPUnit_Framework_Exception + * @throws PHPUnit_Framework_Exception + */ + protected function runTest() + { + if ($this->name === null) { + throw new PHPUnit_Framework_Exception( + 'PHPUnit_Framework_TestCase::$name must not be null.' + ); + } + + try { + $class = new ReflectionClass($this); + $method = $class->getMethod($this->name); + } catch (ReflectionException $e) { + $this->fail($e->getMessage()); + } + + try { + $testResult = $method->invokeArgs( + $this, + array_merge($this->data, $this->dependencyInput) + ); + } catch (Throwable $_e) { + $e = $_e; + } catch (Exception $_e) { + $e = $_e; + } + + if (isset($e)) { + $checkException = false; + + if (is_string($this->expectedException)) { + $checkException = true; + + if ($e instanceof PHPUnit_Framework_Exception) { + $checkException = false; + } + + $reflector = new ReflectionClass($this->expectedException); + + if ($this->expectedException == 'PHPUnit_Framework_Exception' || + $reflector->isSubclassOf('PHPUnit_Framework_Exception')) { + $checkException = true; + } + } + + if ($checkException) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_Exception( + $this->expectedException + ) + ); + + if (is_string($this->expectedExceptionMessage) && + !empty($this->expectedExceptionMessage)) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_ExceptionMessage( + $this->expectedExceptionMessage + ) + ); + } + + if (is_string($this->expectedExceptionMessageRegExp) && + !empty($this->expectedExceptionMessageRegExp)) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_ExceptionMessageRegExp( + $this->expectedExceptionMessageRegExp + ) + ); + } + + if ($this->expectedExceptionCode !== null) { + $this->assertThat( + $e, + new PHPUnit_Framework_Constraint_ExceptionCode( + $this->expectedExceptionCode + ) + ); + } + + return; + } else { + throw $e; + } + } + + if ($this->expectedException !== null) { + $this->assertThat( + null, + new PHPUnit_Framework_Constraint_Exception( + $this->expectedException + ) + ); + } + + return $testResult; + } + + /** + * Verifies the mock object expectations. + * + * @since Method available since Release 3.5.0 + */ + protected function verifyMockObjects() + { + foreach ($this->mockObjects as $mockObject) { + if ($mockObject->__phpunit_hasMatchers()) { + $this->numAssertions++; + } + + $mockObject->__phpunit_verify(); + } + + if ($this->prophet !== null) { + try { + $this->prophet->checkPredictions(); + } catch (Throwable $e) { + /* Intentionally left empty */ + } catch (Exception $e) { + /* Intentionally left empty */ + } + + foreach ($this->prophet->getProphecies() as $objectProphecy) { + foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { + foreach ($methodProphecies as $methodProphecy) { + $this->numAssertions += count($methodProphecy->getCheckedPredictions()); + } + } + } + + if (isset($e)) { + throw $e; + } + } + } + + /** + * Sets the name of a TestCase. + * + * @param string + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Sets the dependencies of a TestCase. + * + * @param array $dependencies + * + * @since Method available since Release 3.4.0 + */ + public function setDependencies(array $dependencies) + { + $this->dependencies = $dependencies; + } + + /** + * Returns true if the tests has dependencies + * + * @return bool + * + * @since Method available since Release 4.0.0 + */ + public function hasDependencies() + { + return count($this->dependencies) > 0; + } + + /** + * Sets + * + * @param array $dependencyInput + * + * @since Method available since Release 3.4.0 + */ + public function setDependencyInput(array $dependencyInput) + { + $this->dependencyInput = $dependencyInput; + } + + /** + * @param bool $disallowChangesToGlobalState + * + * @since Method available since Release 4.6.0 + */ + public function setDisallowChangesToGlobalState($disallowChangesToGlobalState) + { + $this->disallowChangesToGlobalState = $disallowChangesToGlobalState; + } + + /** + * Calling this method in setUp() has no effect! + * + * @param bool $backupGlobals + * + * @since Method available since Release 3.3.0 + */ + public function setBackupGlobals($backupGlobals) + { + if (is_null($this->backupGlobals) && is_bool($backupGlobals)) { + $this->backupGlobals = $backupGlobals; + } + } + + /** + * Calling this method in setUp() has no effect! + * + * @param bool $backupStaticAttributes + * + * @since Method available since Release 3.4.0 + */ + public function setBackupStaticAttributes($backupStaticAttributes) + { + if (is_null($this->backupStaticAttributes) && + is_bool($backupStaticAttributes)) { + $this->backupStaticAttributes = $backupStaticAttributes; + } + } + + /** + * @param bool $runTestInSeparateProcess + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.4.0 + */ + public function setRunTestInSeparateProcess($runTestInSeparateProcess) + { + if (is_bool($runTestInSeparateProcess)) { + if ($this->runTestInSeparateProcess === null) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * @param bool $preserveGlobalState + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.4.0 + */ + public function setPreserveGlobalState($preserveGlobalState) + { + if (is_bool($preserveGlobalState)) { + $this->preserveGlobalState = $preserveGlobalState; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * @param bool $inIsolation + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.4.0 + */ + public function setInIsolation($inIsolation) + { + if (is_bool($inIsolation)) { + $this->inIsolation = $inIsolation; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + + /** + * @return bool + * + * @since Method available since Release 4.3.0 + */ + public function isInIsolation() + { + return $this->inIsolation; + } + + /** + * @return mixed + * + * @since Method available since Release 3.4.0 + */ + public function getResult() + { + return $this->testResult; + } + + /** + * @param mixed $result + * + * @since Method available since Release 3.4.0 + */ + public function setResult($result) + { + $this->testResult = $result; + } + + /** + * @param callable $callback + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.6.0 + */ + public function setOutputCallback($callback) + { + if (!is_callable($callback)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'callback'); + } + + $this->outputCallback = $callback; + } + + /** + * @return PHPUnit_Framework_TestResult + * + * @since Method available since Release 3.5.7 + */ + public function getTestResultObject() + { + return $this->result; + } + + /** + * @param PHPUnit_Framework_TestResult $result + * + * @since Method available since Release 3.6.0 + */ + public function setTestResultObject(PHPUnit_Framework_TestResult $result) + { + $this->result = $result; + } + + /** + * This method is a wrapper for the ini_set() function that automatically + * resets the modified php.ini setting to its original value after the + * test is run. + * + * @param string $varName + * @param string $newValue + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.0.0 + */ + protected function iniSet($varName, $newValue) + { + if (!is_string($varName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $currentValue = ini_set($varName, $newValue); + + if ($currentValue !== false) { + $this->iniSettings[$varName] = $currentValue; + } else { + throw new PHPUnit_Framework_Exception( + sprintf( + 'INI setting "%s" could not be set to "%s".', + $varName, + $newValue + ) + ); + } + } + + /** + * This method is a wrapper for the setlocale() function that automatically + * resets the locale to its original value after the test is run. + * + * @param int $category + * @param string $locale + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.1.0 + */ + protected function setLocale() + { + $args = func_get_args(); + + if (count($args) < 2) { + throw new PHPUnit_Framework_Exception; + } + + $category = $args[0]; + $locale = $args[1]; + + $categories = array( + LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME + ); + + if (defined('LC_MESSAGES')) { + $categories[] = LC_MESSAGES; + } + + if (!in_array($category, $categories)) { + throw new PHPUnit_Framework_Exception; + } + + if (!is_array($locale) && !is_string($locale)) { + throw new PHPUnit_Framework_Exception; + } + + $this->locale[$category] = setlocale($category, null); + + $result = call_user_func_array('setlocale', $args); + + if ($result === false) { + throw new PHPUnit_Framework_Exception( + 'The locale functionality is not implemented on your platform, ' . + 'the specified locale does not exist or the category name is ' . + 'invalid.' + ); + } + } + + /** + * Returns a mock object for the specified class. + * + * @param string $originalClassName Name of the class to mock. + * @param array|null $methods When provided, only methods whose names are in the array + * are replaced with a configurable test double. The behavior + * of the other methods is not changed. + * Providing null means that no methods will be replaced. + * @param array $arguments Parameters to pass to the original class' constructor. + * @param string $mockClassName Class name for the generated test double class. + * @param bool $callOriginalConstructor Can be used to disable the call to the original class' constructor. + * @param bool $callOriginalClone Can be used to disable the call to the original class' clone constructor. + * @param bool $callAutoload Can be used to disable __autoload() during the generation of the test double class. + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * @param object $proxyTarget + * + * @return PHPUnit_Framework_MockObject_MockObject + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.0.0 + */ + public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null) + { + $mockObject = $this->getMockObjectGenerator()->getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments, + $callOriginalMethods, + $proxyTarget + ); + + $this->mockObjects[] = $mockObject; + + return $mockObject; + } + + /** + * Returns a builder object to create mock objects using a fluent interface. + * + * @param string $className + * + * @return PHPUnit_Framework_MockObject_MockBuilder + * + * @since Method available since Release 3.5.0 + */ + public function getMockBuilder($className) + { + return new PHPUnit_Framework_MockObject_MockBuilder($this, $className); + } + + /** + * Mocks the specified class and returns the name of the mocked class. + * + * @param string $originalClassName + * @param array $methods + * @param array $arguments + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * + * @return string + * + * @throws PHPUnit_Framework_Exception + * + * @since Method available since Release 3.5.0 + */ + protected function getMockClass($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false) + { + $mock = $this->getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + + return get_class($mock); + } + + /** + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. Concrete methods are not mocked by default. + * To mock concrete methods, use the 7th parameter ($mockedMethods). + * + * @param string $originalClassName + * @param array $arguments + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @return PHPUnit_Framework_MockObject_MockObject + * + * @since Method available since Release 3.4.0 + * + * @throws PHPUnit_Framework_Exception + */ + public function getMockForAbstractClass($originalClassName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = array(), $cloneArguments = false) + { + $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass( + $originalClassName, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $mockedMethods, + $cloneArguments + ); + + $this->mockObjects[] = $mockObject; + + return $mockObject; + } + + /** + * Returns a mock object based on the given WSDL file. + * + * @param string $wsdlFile + * @param string $originalClassName + * @param string $mockClassName + * @param array $methods + * @param bool $callOriginalConstructor + * @param array $options An array of options passed to SOAPClient::_construct + * + * @return PHPUnit_Framework_MockObject_MockObject + * + * @since Method available since Release 3.4.0 + */ + protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = array(), $callOriginalConstructor = true, array $options = array()) + { + if ($originalClassName === '') { + $originalClassName = str_replace('.wsdl', '', basename($wsdlFile)); + } + + if (!class_exists($originalClassName)) { + eval( + $this->getMockObjectGenerator()->generateClassFromWsdl( + $wsdlFile, + $originalClassName, + $methods, + $options + ) + ); + } + + return $this->getMock( + $originalClassName, + $methods, + array('', $options), + $mockClassName, + $callOriginalConstructor, + false, + false + ); + } + + /** + * Returns a mock object for the specified trait with all abstract methods + * of the trait mocked. Concrete methods to mock can be specified with the + * `$mockedMethods` parameter. + * + * @param string $traitName + * @param array $arguments + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @return PHPUnit_Framework_MockObject_MockObject + * + * @since Method available since Release 4.0.0 + * + * @throws PHPUnit_Framework_Exception + */ + public function getMockForTrait($traitName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = array(), $cloneArguments = false) + { + $mockObject = $this->getMockObjectGenerator()->getMockForTrait( + $traitName, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $mockedMethods, + $cloneArguments + ); + + $this->mockObjects[] = $mockObject; + + return $mockObject; + } + + /** + * Returns an object for the specified trait. + * + * @param string $traitName + * @param array $arguments + * @param string $traitClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * + * @return object + * + * @since Method available since Release 3.6.0 + * + * @throws PHPUnit_Framework_Exception + */ + protected function getObjectForTrait($traitName, array $arguments = array(), $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false) + { + return $this->getMockObjectGenerator()->getObjectForTrait( + $traitName, + $arguments, + $traitClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + } + + /** + * @param string|null $classOrInterface + * + * @return \Prophecy\Prophecy\ObjectProphecy + * + * @throws \LogicException + * + * @since Method available since Release 4.5.0 + */ + protected function prophesize($classOrInterface = null) + { + return $this->getProphet()->prophesize($classOrInterface); + } + + /** + * Adds a value to the assertion counter. + * + * @param int $count + * + * @since Method available since Release 3.3.3 + */ + public function addToAssertionCount($count) + { + $this->numAssertions += $count; + } + + /** + * Returns the number of assertions performed by this test. + * + * @return int + * + * @since Method available since Release 3.3.0 + */ + public function getNumAssertions() + { + return $this->numAssertions; + } + + /** + * Returns a matcher that matches when the method is executed + * zero or more times. + * + * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount + * + * @since Method available since Release 3.0.0 + */ + public static function any() + { + return new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount; + } + + /** + * Returns a matcher that matches when the method is never executed. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * + * @since Method available since Release 3.0.0 + */ + public static function never() + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(0); + } + + /** + * Returns a matcher that matches when the method is executed + * at least N times. + * + * @param int $requiredInvocations + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount + * + * @since Method available since Release 4.2.0 + */ + public static function atLeast($requiredInvocations) + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount( + $requiredInvocations + ); + } + + /** + * Returns a matcher that matches when the method is executed at least once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce + * + * @since Method available since Release 3.0.0 + */ + public static function atLeastOnce() + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce; + } + + /** + * Returns a matcher that matches when the method is executed exactly once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * + * @since Method available since Release 3.0.0 + */ + public static function once() + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(1); + } + + /** + * Returns a matcher that matches when the method is executed + * exactly $count times. + * + * @param int $count + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * + * @since Method available since Release 3.0.0 + */ + public static function exactly($count) + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedCount($count); + } + + /** + * Returns a matcher that matches when the method is executed + * at most N times. + * + * @param int $allowedInvocations + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount + * + * @since Method available since Release 4.2.0 + */ + public static function atMost($allowedInvocations) + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount( + $allowedInvocations + ); + } + + /** + * Returns a matcher that matches when the method is executed + * at the given index. + * + * @param int $index + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex + * + * @since Method available since Release 3.0.0 + */ + public static function at($index) + { + return new PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex($index); + } + + /** + * @param mixed $value + * + * @return PHPUnit_Framework_MockObject_Stub_Return + * + * @since Method available since Release 3.0.0 + */ + public static function returnValue($value) + { + return new PHPUnit_Framework_MockObject_Stub_Return($value); + } + + /** + * @param array $valueMap + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap + * + * @since Method available since Release 3.6.0 + */ + public static function returnValueMap(array $valueMap) + { + return new PHPUnit_Framework_MockObject_Stub_ReturnValueMap($valueMap); + } + + /** + * @param int $argumentIndex + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument + * + * @since Method available since Release 3.3.0 + */ + public static function returnArgument($argumentIndex) + { + return new PHPUnit_Framework_MockObject_Stub_ReturnArgument( + $argumentIndex + ); + } + + /** + * @param mixed $callback + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback + * + * @since Method available since Release 3.3.0 + */ + public static function returnCallback($callback) + { + return new PHPUnit_Framework_MockObject_Stub_ReturnCallback($callback); + } + + /** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf + * + * @since Method available since Release 3.6.0 + */ + public static function returnSelf() + { + return new PHPUnit_Framework_MockObject_Stub_ReturnSelf(); + } + + /** + * @param Exception $exception + * + * @return PHPUnit_Framework_MockObject_Stub_Exception + * + * @since Method available since Release 3.1.0 + */ + public static function throwException(Exception $exception) + { + return new PHPUnit_Framework_MockObject_Stub_Exception($exception); + } + + /** + * @param mixed $value, ... + * + * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls + * + * @since Method available since Release 3.0.0 + */ + public static function onConsecutiveCalls() + { + $args = func_get_args(); + + return new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($args); + } + + /** + * Gets the data set description of a TestCase. + * + * @param bool $includeData + * + * @return string + * + * @since Method available since Release 3.3.0 + */ + protected function getDataSetAsString($includeData = true) + { + $buffer = ''; + + if (!empty($this->data)) { + if (is_int($this->dataName)) { + $buffer .= sprintf(' with data set #%d', $this->dataName); + } else { + $buffer .= sprintf(' with data set "%s"', $this->dataName); + } + + $exporter = new Exporter; + + if ($includeData) { + $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); + } + } + + return $buffer; + } + + /** + * Creates a default TestResult object. + * + * @return PHPUnit_Framework_TestResult + */ + protected function createResult() + { + return new PHPUnit_Framework_TestResult; + } + + /** + * @since Method available since Release 3.5.4 + */ + protected function handleDependencies() + { + if (!empty($this->dependencies) && !$this->inIsolation) { + $className = get_class($this); + $passed = $this->result->passed(); + $passedKeys = array_keys($passed); + $numKeys = count($passedKeys); + + for ($i = 0; $i < $numKeys; $i++) { + $pos = strpos($passedKeys[$i], ' with data set'); + + if ($pos !== false) { + $passedKeys[$i] = substr($passedKeys[$i], 0, $pos); + } + } + + $passedKeys = array_flip(array_unique($passedKeys)); + + foreach ($this->dependencies as $dependency) { + if (strpos($dependency, '::') === false) { + $dependency = $className . '::' . $dependency; + } + + if (!isset($passedKeys[$dependency])) { + $this->result->addError( + $this, + new PHPUnit_Framework_SkippedTestError( + sprintf( + 'This test depends on "%s" to pass.', + $dependency + ) + ), + 0 + ); + + return false; + } + + if (isset($passed[$dependency])) { + if ($passed[$dependency]['size'] != PHPUnit_Util_Test::UNKNOWN && + $this->getSize() != PHPUnit_Util_Test::UNKNOWN && + $passed[$dependency]['size'] > $this->getSize()) { + $this->result->addError( + $this, + new PHPUnit_Framework_SkippedTestError( + 'This test depends on a test that is larger than itself.' + ), + 0 + ); + + return false; + } + + $this->dependencyInput[$dependency] = $passed[$dependency]['result']; + } else { + $this->dependencyInput[$dependency] = null; + } + } + } + + return true; + } + + /** + * This method is called before the first test of this test class is run. + * + * @since Method available since Release 3.4.0 + */ + public static function setUpBeforeClass() + { + } + + /** + * Sets up the fixture, for example, open a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + } + + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called before the execution of a test starts + * and after setUp() is called. + * + * @since Method available since Release 3.2.8 + */ + protected function assertPreConditions() + { + } + + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called before the execution of a test ends + * and before tearDown() is called. + * + * @since Method available since Release 3.2.8 + */ + protected function assertPostConditions() + { + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + + /** + * This method is called after the last test of this test class is run. + * + * @since Method available since Release 3.4.0 + */ + public static function tearDownAfterClass() + { + } + + /** + * This method is called when a test method did not execute successfully. + * + * @param Exception $e + * + * @since Method available since Release 3.4.0 + * + * @throws Exception + */ + protected function onNotSuccessfulTest(Exception $e) + { + throw $e; + } + + /** + * Performs custom preparations on the process isolation template. + * + * @param Text_Template $template + * + * @since Method available since Release 3.4.0 + */ + protected function prepareTemplate(Text_Template $template) + { + } + + /** + * Get the mock object generator, creating it if it doesn't exist. + * + * @return PHPUnit_Framework_MockObject_Generator + */ + protected function getMockObjectGenerator() + { + if (null === $this->mockObjectGenerator) { + $this->mockObjectGenerator = new PHPUnit_Framework_MockObject_Generator; + } + + return $this->mockObjectGenerator; + } + + /** + * @since Method available since Release 4.2.0 + */ + private function startOutputBuffering() + { + while (!defined('PHPUNIT_TESTSUITE') && ob_get_level() > 0) { + ob_end_clean(); + } + + ob_start(); + + $this->outputBufferingActive = true; + $this->outputBufferingLevel = ob_get_level(); + } + + /** + * @since Method available since Release 4.2.0 + */ + private function stopOutputBuffering() + { + if (ob_get_level() != $this->outputBufferingLevel) { + while (ob_get_level() > 0) { + ob_end_clean(); + } + + throw new PHPUnit_Framework_RiskyTestError( + 'Test code or tested code did not (only) close its own output buffers' + ); + } + + $output = ob_get_contents(); + + if ($this->outputCallback === false) { + $this->output = $output; + } else { + $this->output = call_user_func_array( + $this->outputCallback, + array($output) + ); + } + + ob_end_clean(); + + $this->outputBufferingActive = false; + $this->outputBufferingLevel = ob_get_level(); + } + + private function snapshotGlobalState() + { + $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; + + if ($this->runTestInSeparateProcess || $this->inIsolation || + (!$backupGlobals && !$this->backupStaticAttributes)) { + return; + } + + $this->snapshot = $this->createGlobalStateSnapshot($backupGlobals); + } + + private function restoreGlobalState() + { + if (!$this->snapshot instanceof Snapshot) { + return; + } + + $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; + + if ($this->disallowChangesToGlobalState) { + try { + $this->compareGlobalStateSnapshots( + $this->snapshot, + $this->createGlobalStateSnapshot($backupGlobals) + ); + } catch (PHPUnit_Framework_RiskyTestError $rte) { + // Intentionally left empty + } + } + + $restorer = new Restorer; + + if ($backupGlobals) { + $restorer->restoreGlobalVariables($this->snapshot); + } + + if ($this->backupStaticAttributes) { + $restorer->restoreStaticAttributes($this->snapshot); + } + + $this->snapshot = null; + + if (isset($rte)) { + throw $rte; + } + } + + /** + * @param bool $backupGlobals + * + * @return Snapshot + */ + private function createGlobalStateSnapshot($backupGlobals) + { + $blacklist = new Blacklist; + + foreach ($this->backupGlobalsBlacklist as $globalVariable) { + $blacklist->addGlobalVariable($globalVariable); + } + + if (!defined('PHPUNIT_TESTSUITE')) { + $blacklist->addClassNamePrefix('PHPUnit'); + $blacklist->addClassNamePrefix('File_Iterator'); + $blacklist->addClassNamePrefix('PHP_CodeCoverage'); + $blacklist->addClassNamePrefix('PHP_Invoker'); + $blacklist->addClassNamePrefix('PHP_Timer'); + $blacklist->addClassNamePrefix('PHP_Token'); + $blacklist->addClassNamePrefix('Symfony'); + $blacklist->addClassNamePrefix('Text_Template'); + $blacklist->addClassNamePrefix('Doctrine\Instantiator'); + + foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { + foreach ($attributes as $attribute) { + $blacklist->addStaticAttribute($class, $attribute); + } + } + } + + return new Snapshot( + $blacklist, + $backupGlobals, + $this->backupStaticAttributes, + false, + false, + false, + false, + false, + false, + false + ); + } + + /** + * @param Snapshot $before + * @param Snapshot $after + * + * @throws PHPUnit_Framework_RiskyTestError + */ + private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after) + { + $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; + + if ($backupGlobals) { + $this->compareGlobalStateSnapshotPart( + $before->globalVariables(), + $after->globalVariables(), + "--- Global variables before the test\n+++ Global variables after the test\n" + ); + + $this->compareGlobalStateSnapshotPart( + $before->superGlobalVariables(), + $after->superGlobalVariables(), + "--- Super-global variables before the test\n+++ Super-global variables after the test\n" + ); + } + + if ($this->backupStaticAttributes) { + $this->compareGlobalStateSnapshotPart( + $before->staticAttributes(), + $after->staticAttributes(), + "--- Static attributes before the test\n+++ Static attributes after the test\n" + ); + } + } + + /** + * @param array $before + * @param array $after + * @param string $header + * + * @throws PHPUnit_Framework_RiskyTestError + */ + private function compareGlobalStateSnapshotPart(array $before, array $after, $header) + { + if ($before != $after) { + $differ = new Differ($header); + $exporter = new Exporter; + + $diff = $differ->diff( + $exporter->export($before), + $exporter->export($after) + ); + + throw new PHPUnit_Framework_RiskyTestError( + $diff + ); + } + } + + /** + * @return Prophecy\Prophet + * + * @since Method available since Release 4.5.0 + */ + private function getProphet() + { + if ($this->prophet === null) { + $this->prophet = new Prophet; + } + + return $this->prophet; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A marker interface for marking a unit test as being skipped. + * + * @since Interface available since Release 3.0.0 + */ +interface PHPUnit_Framework_SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a skipped test. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_SkippedTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wraps Exceptions thrown by code under test. + * + * Re-instantiates Exceptions thrown by user-space code to retain their original + * class names, properties, and stack traces (but without arguments). + * + * Unlike PHPUnit_Framework_Exception, the complete stack of previous Exceptions + * is processed. + * + * @since Class available since Release 4.3.0 + */ +class PHPUnit_Framework_ExceptionWrapper extends PHPUnit_Framework_Exception +{ + /** + * @var string + */ + protected $classname; + + /** + * @var PHPUnit_Framework_ExceptionWrapper|null + */ + protected $previous; + + /** + * @param Throwable|Exception $e + */ + public function __construct($e) + { + // PDOException::getCode() is a string. + // @see http://php.net/manual/en/class.pdoexception.php#95812 + parent::__construct($e->getMessage(), (int) $e->getCode()); + + $this->classname = get_class($e); + $this->file = $e->getFile(); + $this->line = $e->getLine(); + + $this->serializableTrace = $e->getTrace(); + + foreach ($this->serializableTrace as $i => $call) { + unset($this->serializableTrace[$i]['args']); + } + + if ($e->getPrevious()) { + $this->previous = new self($e->getPrevious()); + } + } + + /** + * @return string + */ + public function getClassname() + { + return $this->classname; + } + + /** + * @return PHPUnit_Framework_ExceptionWrapper + */ + public function getPreviousWrapped() + { + return $this->previous; + } + + /** + * @return string + */ + public function __toString() + { + $string = PHPUnit_Framework_TestFailure::exceptionToString($this); + + if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; + } + + if ($this->previous) { + $string .= "\nCaused by\n" . $this->previous; + } + + return $string; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of a skipped test suite. + * + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Framework_SkippedTestSuiteError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A TestFailure collects a failed test together with the caught exception. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_TestFailure +{ + /** + * @var string + */ + private $testName; + + /** + * @var PHPUnit_Framework_Test|null + */ + protected $failedTest; + + /** + * @var Exception + */ + protected $thrownException; + + /** + * Constructs a TestFailure with the given test and exception. + * + * @param PHPUnit_Framework_Test $failedTest + * @param Exception $thrownException + */ + public function __construct(PHPUnit_Framework_Test $failedTest, Exception $thrownException) + { + if ($failedTest instanceof PHPUnit_Framework_SelfDescribing) { + $this->testName = $failedTest->toString(); + } else { + $this->testName = get_class($failedTest); + } + if (!$failedTest instanceof PHPUnit_Framework_TestCase || !$failedTest->isInIsolation()) { + $this->failedTest = $failedTest; + } + $this->thrownException = $thrownException; + } + + /** + * Returns a short description of the failure. + * + * @return string + */ + public function toString() + { + return sprintf( + '%s: %s', + $this->testName, + $this->thrownException->getMessage() + ); + } + + /** + * Returns a description for the thrown exception. + * + * @return string + * + * @since Method available since Release 3.4.0 + */ + public function getExceptionAsString() + { + return self::exceptionToString($this->thrownException); + } + + /** + * Returns a description for an exception. + * + * @param Exception $e + * + * @return string + * + * @since Method available since Release 3.2.0 + */ + public static function exceptionToString(Exception $e) + { + if ($e instanceof PHPUnit_Framework_SelfDescribing) { + $buffer = $e->toString(); + + if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) { + $buffer = $buffer . $e->getComparisonFailure()->getDiff(); + } + + if (!empty($buffer)) { + $buffer = trim($buffer) . "\n"; + } + } elseif ($e instanceof PHPUnit_Framework_Error) { + $buffer = $e->getMessage() . "\n"; + } elseif ($e instanceof PHPUnit_Framework_ExceptionWrapper) { + $buffer = $e->getClassname() . ': ' . $e->getMessage() . "\n"; + } else { + $buffer = get_class($e) . ': ' . $e->getMessage() . "\n"; + } + + return $buffer; + } + + /** + * Returns the name of the failing test (including data set, if any). + * + * @return string + * + * @since Method available since Release 4.3.0 + */ + public function getTestName() + { + return $this->testName; + } + + /** + * Returns the failing test. + * + * Note: The test object is not set when the test is executed in process + * isolation. + * + * @see PHPUnit_Framework_Exception + * + * @return PHPUnit_Framework_Test|null + */ + public function failedTest() + { + return $this->failedTest; + } + + /** + * Gets the thrown exception. + * + * @return Exception + */ + public function thrownException() + { + return $this->thrownException; + } + + /** + * Returns the exception's message. + * + * @return string + */ + public function exceptionMessage() + { + return $this->thrownException()->getMessage(); + } + + /** + * Returns true if the thrown exception + * is of type AssertionFailedError. + * + * @return bool + */ + public function isFailure() + { + return ($this->thrownException() instanceof PHPUnit_Framework_AssertionFailedError); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the object it is evaluated for is an instance + * of a given class. + * + * The expected class name is passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsInstanceOf extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $className; + + /** + * @param string $className + */ + public function __construct($className) + { + parent::__construct(); + $this->className = $className; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return ($other instanceof $this->className); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + '%s is an instance of %s "%s"', + $this->exporter->shortenedExport($other), + $this->getType(), + $this->className + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'is instance of %s "%s"', + $this->getType(), + $this->className + ); + } + + private function getType() + { + try { + $reflection = new ReflectionClass($this->className); + if ($reflection->isInterface()) { + return 'interface'; + } + } catch (ReflectionException $e) { + } + + return 'class'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the array it is evaluated for has a specified subset. + * + * Uses array_replace_recursive() to check if a key value subset is part of the + * subject array. + * + * @since Class available since Release 4.4.0 + */ +class PHPUnit_Framework_Constraint_ArraySubset extends PHPUnit_Framework_Constraint +{ + /** + * @var array|ArrayAccess + */ + protected $subset; + + /** + * @var bool + */ + protected $strict; + + /** + * @param array|ArrayAccess $subset + * @param bool $strict Check for object identity + */ + public function __construct($subset, $strict = false) + { + parent::__construct(); + $this->strict = $strict; + $this->subset = $subset; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param array|ArrayAccess $other Array or ArrayAccess object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + $patched = array_replace_recursive($other, $this->subset); + + if ($this->strict) { + return $other === $patched; + } else { + return $other == $patched; + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'has the subset ' . $this->exporter->export($this->subset); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return 'an array ' . $this->toString(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that checks whether a variable is empty(). + * + * @since Class available since Release 3.5.0 + */ +class PHPUnit_Framework_Constraint_IsEmpty extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + if ($other instanceof Countable) { + return count($other) === 0; + } + + return empty($other); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is empty'; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + $type = gettype($other); + + return sprintf( + '%s %s %s', + $type[0] == 'a' || $type[0] == 'o' ? 'an' : 'a', + $type, + $this->toString() + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Logical XOR. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_Xor extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint[] + */ + protected $constraints = array(); + + /** + * @param PHPUnit_Framework_Constraint[] $constraints + */ + public function setConstraints(array $constraints) + { + $this->constraints = array(); + + foreach ($constraints as $constraint) { + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + $constraint = new PHPUnit_Framework_Constraint_IsEqual( + $constraint + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = true; + $lastResult = null; + $constraint = null; + + foreach ($this->constraints as $constraint) { + $result = $constraint->evaluate($other, $description, true); + + if ($result === $lastResult) { + $success = false; + break; + } + + $lastResult = $result; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' xor '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + * + * @return int + * + * @since Method available since Release 3.4.0 + */ + public function count() + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += count($constraint); + } + + return $count; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.1.0 + */ +abstract class PHPUnit_Framework_Constraint_Composite extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint + */ + protected $innerConstraint; + + /** + * @param PHPUnit_Framework_Constraint $innerConstraint + */ + public function __construct(PHPUnit_Framework_Constraint $innerConstraint) + { + parent::__construct(); + $this->innerConstraint = $innerConstraint; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + try { + return $this->innerConstraint->evaluate( + $other, + $description, + $returnResult + ); + } catch (PHPUnit_Framework_ExpectationFailedException $e) { + $this->fail($other, $description); + } + } + + /** + * Counts the number of constraint elements. + * + * @return int + */ + public function count() + { + return count($this->innerConstraint); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the Traversable it is applied to contains + * a given value. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_TraversableContains extends PHPUnit_Framework_Constraint +{ + /** + * @var bool + */ + protected $checkForObjectIdentity; + + /** + * @var bool + */ + protected $checkForNonObjectIdentity; + + /** + * @var mixed + */ + protected $value; + + /** + * @param mixed $value + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) + { + parent::__construct(); + + if (!is_bool($checkForObjectIdentity)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean'); + } + + if (!is_bool($checkForNonObjectIdentity)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'boolean'); + } + + $this->checkForObjectIdentity = $checkForObjectIdentity; + $this->checkForNonObjectIdentity = $checkForNonObjectIdentity; + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + if ($other instanceof SplObjectStorage) { + return $other->contains($this->value); + } + + if (is_object($this->value)) { + foreach ($other as $element) { + if ($this->checkForObjectIdentity && $element === $this->value) { + return true; + } elseif (!$this->checkForObjectIdentity && $element == $this->value) { + return true; + } + } + } else { + foreach ($other as $element) { + if ($this->checkForNonObjectIdentity && $element === $this->value) { + return true; + } elseif (!$this->checkForNonObjectIdentity && $element == $this->value) { + return true; + } + } + } + + return false; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + if (is_string($this->value) && strpos($this->value, "\n") !== false) { + return 'contains "' . $this->value . '"'; + } else { + return 'contains ' . $this->exporter->export($this->value); + } + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + '%s %s', + is_array($other) ? 'an array' : 'a traversable', + $this->toString() + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Constraint_Count extends PHPUnit_Framework_Constraint +{ + /** + * @var int + */ + protected $expectedCount = 0; + + /** + * @param int $expected + */ + public function __construct($expected) + { + parent::__construct(); + $this->expectedCount = $expected; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other + * + * @return bool + */ + protected function matches($other) + { + return $this->expectedCount === $this->getCountOf($other); + } + + /** + * @param mixed $other + * + * @return bool + */ + protected function getCountOf($other) + { + if ($other instanceof Countable || is_array($other)) { + return count($other); + } elseif ($other instanceof Traversable) { + if ($other instanceof IteratorAggregate) { + $iterator = $other->getIterator(); + } else { + $iterator = $other; + } + + $key = $iterator->key(); + $count = iterator_count($iterator); + + // manually rewind $iterator to previous key, since iterator_count + // moves pointer + if ($key !== null) { + $iterator->rewind(); + while ($iterator->valid() && $key !== $iterator->key()) { + $iterator->next(); + } + } + + return $count; + } + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + 'actual size %d matches expected size %d', + $this->getCountOf($other), + $this->expectedCount + ); + } + + /** + * @return string + */ + public function toString() + { + return sprintf( + 'count matches %d', + $this->expectedCount + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that one value is identical to another. + * + * Identical check is performed with PHP's === operator, the operator is + * explained in detail at + * {@url http://www.php.net/manual/en/types.comparisons.php}. + * Two values are identical if they have the same value and are of the same + * type. + * + * The expected value is passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsIdentical extends PHPUnit_Framework_Constraint +{ + /** + * @var float + */ + const EPSILON = 0.0000000001; + + /** + * @var mixed + */ + protected $value; + + /** + * @param mixed $value + */ + public function __construct($value) + { + parent::__construct(); + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + if (is_double($this->value) && is_double($other) && + !is_infinite($this->value) && !is_infinite($other) && + !is_nan($this->value) && !is_nan($other)) { + $success = abs($this->value - $other) < self::EPSILON; + } else { + $success = $this->value === $other; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $f = null; + + // if both values are strings, make sure a diff is generated + if (is_string($this->value) && is_string($other)) { + $f = new SebastianBergmann\Comparator\ComparisonFailure( + $this->value, + $other, + $this->value, + $other + ); + } + + $this->fail($other, $description, $f); + } + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + if (is_object($this->value) && is_object($other)) { + return 'two variables reference the same object'; + } + + if (is_string($this->value) && is_string($other)) { + return 'two strings are identical'; + } + + return parent::failureDescription($other); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + if (is_object($this->value)) { + return 'is identical to an object of class "' . + get_class($this->value) . '"'; + } else { + return 'is identical to ' . + $this->exporter->export($this->value); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the string it is evaluated for ends with a given + * suffix. + * + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_Constraint_StringEndsWith extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $suffix; + + /** + * @param string $suffix + */ + public function __construct($suffix) + { + parent::__construct(); + $this->suffix = $suffix; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return substr($other, 0 - strlen($this->suffix)) == $this->suffix; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'ends with "' . $this->suffix . '"'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the value it is evaluated for is less than + * a given value. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_LessThan extends PHPUnit_Framework_Constraint +{ + /** + * @var numeric + */ + protected $value; + + /** + * @param numeric $value + */ + public function __construct($value) + { + parent::__construct(); + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return $this->value > $other; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is less than ' . $this->exporter->export($this->value); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.6.6 + */ +class PHPUnit_Framework_Constraint_ExceptionMessage extends PHPUnit_Framework_Constraint +{ + /** + * @var int + */ + protected $expectedMessage; + + /** + * @param string $expected + */ + public function __construct($expected) + { + parent::__construct(); + $this->expectedMessage = $expected; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param Exception $other + * + * @return bool + */ + protected function matches($other) + { + return strpos($other->getMessage(), $this->expectedMessage) !== false; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + "exception message '%s' contains '%s'", + $other->getMessage(), + $this->expectedMessage + ); + } + + /** + * @return string + */ + public function toString() + { + return 'exception message contains '; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the value it is evaluated for is greater + * than a given value. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_GreaterThan extends PHPUnit_Framework_Constraint +{ + /** + * @var numeric + */ + protected $value; + + /** + * @param numeric $value + */ + public function __construct($value) + { + parent::__construct(); + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return $this->value < $other; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is greater than ' . $this->exporter->export($this->value); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.3.0 + */ +class PHPUnit_Framework_Constraint_ExceptionMessageRegExp extends PHPUnit_Framework_Constraint +{ + /** + * @var int + */ + protected $expectedMessageRegExp; + + /** + * @param string $expected + */ + public function __construct($expected) + { + parent::__construct(); + $this->expectedMessageRegExp = $expected; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param Exception $other + * + * @return bool + */ + protected function matches($other) + { + $match = PHPUnit_Util_Regex::pregMatchSafe($this->expectedMessageRegExp, $other->getMessage()); + + if (false === $match) { + throw new PHPUnit_Framework_Exception( + "Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'" + ); + } + + return 1 === $match; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + "exception message '%s' matches '%s'", + $other->getMessage(), + $this->expectedMessageRegExp + ); + } + + /** + * @return string + */ + public function toString() + { + return 'exception message matches '; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that evaluates against a specified closure. + */ +class PHPUnit_Framework_Constraint_Callback extends PHPUnit_Framework_Constraint +{ + private $callback; + + /** + * @param callable $callback + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory( + 1, + 'callable' + ); + } + + parent::__construct(); + + $this->callback = $callback; + } + + /** + * Evaluates the constraint for parameter $value. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return call_user_func($this->callback, $other); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is accepted by specified callback'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that a string is valid JSON. + * + * @since Class available since Release 3.7.20 + */ +class PHPUnit_Framework_Constraint_IsJson extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + json_decode($other); + if (json_last_error()) { + return false; + } + + return true; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + json_decode($other); + $error = PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider::determineJsonError( + json_last_error() + ); + + return sprintf( + '%s is valid JSON (%s)', + $this->exporter->shortenedExport($other), + $error + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is valid JSON'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the array it is evaluated for has a given key. + * + * Uses array_key_exists() to check if the key is found in the input array, if + * not found the evaluation fails. + * + * The array key is passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_ArrayHasKey extends PHPUnit_Framework_Constraint +{ + /** + * @var int|string + */ + protected $key; + + /** + * @param int|string $key + */ + public function __construct($key) + { + parent::__construct(); + $this->key = $key; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + if (is_array($other)) { + return array_key_exists($this->key, $other); + } + + if ($other instanceof ArrayAccess) { + return $other->offsetExists($this->key); + } + + return false; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'has the key ' . $this->exporter->export($this->key); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return 'an array ' . $this->toString(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that accepts false. + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Constraint_IsFalse extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return $other === false; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is false'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that checks if the file(name) that it is evaluated for exists. + * + * The file path to check is passed as $other in evaluate(). + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_FileExists extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return file_exists($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + 'file "%s" exists', + $other + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'file exists'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the string it is evaluated for begins with a + * given prefix. + * + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_Constraint_StringStartsWith extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $prefix; + + /** + * @param string $prefix + */ + public function __construct($prefix) + { + parent::__construct(); + $this->prefix = $prefix; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return strpos($other, $this->prefix) === 0; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'starts with "' . $this->prefix . '"'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Provides human readable messages for each JSON error. + * + * @since Class available since Release 3.7.0 + */ +class PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider +{ + /** + * Translates JSON error to a human readable string. + * + * @param string $error + * @param string $prefix + * + * @return string + */ + public static function determineJsonError($error, $prefix = '') + { + switch ($error) { + case JSON_ERROR_NONE: + return; + case JSON_ERROR_DEPTH: + return $prefix . 'Maximum stack depth exceeded'; + case JSON_ERROR_STATE_MISMATCH: + return $prefix . 'Underflow or the modes mismatch'; + case JSON_ERROR_CTRL_CHAR: + return $prefix . 'Unexpected control character found'; + case JSON_ERROR_SYNTAX: + return $prefix . 'Syntax error, malformed JSON'; + case JSON_ERROR_UTF8: + return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded'; + default: + return $prefix . 'Unknown error'; + } + } + + /** + * Translates a given type to a human readable message prefix. + * + * @param string $type + * + * @return string + */ + public static function translateTypeToPrefix($type) + { + switch (strtolower($type)) { + case 'expected': + $prefix = 'Expected value JSON decode error - '; + break; + case 'actual': + $prefix = 'Actual value JSON decode error - '; + break; + default: + $prefix = ''; + break; + } + + return $prefix; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the string it is evaluated for matches + * a regular expression. + * + * Checks a given value using the Perl Compatible Regular Expression extension + * in PHP. The pattern is matched by executing preg_match(). + * + * The pattern string passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_PCREMatch extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $pattern; + + /** + * @param string $pattern + */ + public function __construct($pattern) + { + parent::__construct(); + $this->pattern = $pattern; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return preg_match($this->pattern, $other) > 0; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'matches PCRE pattern "%s"', + $this->pattern + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the class it is evaluated for has a given + * attribute. + * + * The attribute name is passed in the constructor. + * + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Framework_Constraint_ClassHasAttribute extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $attributeName; + + /** + * @param string $attributeName + */ + public function __construct($attributeName) + { + parent::__construct(); + $this->attributeName = $attributeName; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + $class = new ReflectionClass($other); + + return $class->hasProperty($this->attributeName); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'has attribute "%s"', + $this->attributeName + ); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + '%sclass "%s" %s', + is_object($other) ? 'object of ' : '', + is_object($other) ? get_class($other) : $other, + $this->toString() + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use SebastianBergmann\Diff\Differ; + +/** + * ... + * + * @since Class available since Release 3.5.0 + */ +class PHPUnit_Framework_Constraint_StringMatches extends PHPUnit_Framework_Constraint_PCREMatch +{ + /** + * @var string + */ + protected $string; + + /** + * @param string $string + */ + public function __construct($string) + { + parent::__construct($string); + + $this->pattern = $this->createPatternFromFormat( + preg_replace('/\r\n/', "\n", $string) + ); + + $this->string = $string; + } + + protected function failureDescription($other) + { + return 'format description matches text'; + } + + protected function additionalFailureDescription($other) + { + $from = preg_split('(\r\n|\r|\n)', $this->string); + $to = preg_split('(\r\n|\r|\n)', $other); + + foreach ($from as $index => $line) { + if (isset($to[$index]) && $line !== $to[$index]) { + $line = $this->createPatternFromFormat($line); + + if (preg_match($line, $to[$index]) > 0) { + $from[$index] = $to[$index]; + } + } + } + + $this->string = implode("\n", $from); + $other = implode("\n", $to); + + $differ = new Differ("--- Expected\n+++ Actual\n"); + + return $differ->diff($this->string, $other); + } + + protected function createPatternFromFormat($string) + { + $string = str_replace( + array( + '%e', + '%s', + '%S', + '%a', + '%A', + '%w', + '%i', + '%d', + '%x', + '%f', + '%c' + ), + array( + '\\' . DIRECTORY_SEPARATOR, + '[^\r\n]+', + '[^\r\n]*', + '.+', + '.*', + '\s*', + '[+-]?\d+', + '\d+', + '[0-9a-fA-F]+', + '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', + '.' + ), + preg_quote($string, '/') + ); + + return '/^' . $string . '$/s'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.6.6 + */ +class PHPUnit_Framework_Constraint_ExceptionCode extends PHPUnit_Framework_Constraint +{ + /** + * @var int + */ + protected $expectedCode; + + /** + * @param int $expected + */ + public function __construct($expected) + { + parent::__construct(); + $this->expectedCode = $expected; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param Exception $other + * + * @return bool + */ + protected function matches($other) + { + return (string) $other->getCode() == (string) $this->expectedCode; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return sprintf( + '%s is equal to expected exception code %s', + $this->exporter->export($other->getCode()), + $this->exporter->export($this->expectedCode) + ); + } + + /** + * @return string + */ + public function toString() + { + return 'exception code is '; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.6.0 + */ +class PHPUnit_Framework_Constraint_SameSize extends PHPUnit_Framework_Constraint_Count +{ + /** + * @var int + */ + protected $expectedCount; + + /** + * @param int $expected + */ + public function __construct($expected) + { + parent::__construct($this->getCountOf($expected)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that accepts any input value. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsAnything extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + return $returnResult ? true : null; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is anything'; + } + + /** + * Counts the number of constraint elements. + * + * @return int + * + * @since Method available since Release 3.5.0 + */ + public function count() + { + return 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the value it is evaluated for is of a + * specified type. + * + * The expected value is passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsType extends PHPUnit_Framework_Constraint +{ + const TYPE_ARRAY = 'array'; + const TYPE_BOOL = 'bool'; + const TYPE_FLOAT = 'float'; + const TYPE_INT = 'int'; + const TYPE_NULL = 'null'; + const TYPE_NUMERIC = 'numeric'; + const TYPE_OBJECT = 'object'; + const TYPE_RESOURCE = 'resource'; + const TYPE_STRING = 'string'; + const TYPE_SCALAR = 'scalar'; + const TYPE_CALLABLE = 'callable'; + + /** + * @var array + */ + protected $types = array( + 'array' => true, + 'boolean' => true, + 'bool' => true, + 'double' => true, + 'float' => true, + 'integer' => true, + 'int' => true, + 'null' => true, + 'numeric' => true, + 'object' => true, + 'real' => true, + 'resource' => true, + 'string' => true, + 'scalar' => true, + 'callable' => true + ); + + /** + * @var string + */ + protected $type; + + /** + * @param string $type + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($type) + { + parent::__construct(); + + if (!isset($this->types[$type])) { + throw new PHPUnit_Framework_Exception( + sprintf( + 'Type specified for PHPUnit_Framework_Constraint_IsType <%s> ' . + 'is not a valid type.', + $type + ) + ); + } + + $this->type = $type; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + switch ($this->type) { + case 'numeric': + return is_numeric($other); + + case 'integer': + case 'int': + return is_integer($other); + + case 'double': + case 'float': + case 'real': + return is_float($other); + + case 'string': + return is_string($other); + + case 'boolean': + case 'bool': + return is_bool($other); + + case 'null': + return is_null($other); + + case 'array': + return is_array($other); + + case 'object': + return is_object($other); + + case 'resource': + return is_resource($other) || is_string(@get_resource_type($other)); + + case 'scalar': + return is_scalar($other); + + case 'callable': + return is_callable($other); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'is of type "%s"', + $this->type + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the string it is evaluated for contains + * a given string. + * + * Uses strpos() to find the position of the string in the input, if not found + * the evaluation fails. + * + * The sub-string is passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_StringContains extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $string; + + /** + * @var bool + */ + protected $ignoreCase; + + /** + * @param string $string + * @param bool $ignoreCase + */ + public function __construct($string, $ignoreCase = false) + { + parent::__construct(); + + $this->string = $string; + $this->ignoreCase = $ignoreCase; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + if ($this->ignoreCase) { + return stripos($other, $this->string) !== false; + } else { + return strpos($other, $this->string) !== false; + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + if ($this->ignoreCase) { + $string = strtolower($this->string); + } else { + $string = $this->string; + } + + return sprintf( + 'contains "%s"', + $string + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Framework_Constraint_Attribute extends PHPUnit_Framework_Constraint_Composite +{ + /** + * @var string + */ + protected $attributeName; + + /** + * @param PHPUnit_Framework_Constraint $constraint + * @param string $attributeName + */ + public function __construct(PHPUnit_Framework_Constraint $constraint, $attributeName) + { + parent::__construct($constraint); + + $this->attributeName = $attributeName; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + return parent::evaluate( + PHPUnit_Framework_Assert::readAttribute( + $other, + $this->attributeName + ), + $description, + $returnResult + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'attribute "' . $this->attributeName . '" ' . + $this->innerConstraint->toString(); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + return $this->toString(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the object it is evaluated for has a given + * attribute. + * + * The attribute name is passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_ObjectHasAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + $object = new ReflectionObject($other); + + return $object->hasProperty($this->attributeName); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that accepts true. + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return $other === true; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is true'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Asserts whether or not two JSON objects are equal. + * + * @since Class available since Release 3.7.0 + */ +class PHPUnit_Framework_Constraint_JsonMatches extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $value; + + /** + * Creates a new constraint. + * + * @param string $value + */ + public function __construct($value) + { + parent::__construct(); + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + $decodedOther = json_decode($other); + if (json_last_error()) { + return false; + } + + $decodedValue = json_decode($this->value); + if (json_last_error()) { + return false; + } + + return $decodedOther == $decodedValue; + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return sprintf( + 'matches JSON string "%s"', + $this->value + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the Traversable it is applied to contains + * only values of a given type. + * + * @since Class available since Release 3.1.4 + */ +class PHPUnit_Framework_Constraint_TraversableContainsOnly extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint + */ + protected $constraint; + + /** + * @var string + */ + protected $type; + + /** + * @param string $type + * @param bool $isNativeType + */ + public function __construct($type, $isNativeType = true) + { + parent::__construct(); + + if ($isNativeType) { + $this->constraint = new PHPUnit_Framework_Constraint_IsType($type); + } else { + $this->constraint = new PHPUnit_Framework_Constraint_IsInstanceOf( + $type + ); + } + + $this->type = $type; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = true; + + foreach ($other as $item) { + if (!$this->constraint->evaluate($item, '', true)) { + $success = false; + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'contains only values of type "' . $this->type . '"'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that checks if one value is equal to another. + * + * Equality is checked with PHP's == operator, the operator is explained in + * detail at {@url http://www.php.net/manual/en/types.comparisons.php}. + * Two values are equal if they have the same value disregarding type. + * + * The expected value is passed in the constructor. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_IsEqual extends PHPUnit_Framework_Constraint +{ + /** + * @var mixed + */ + protected $value; + + /** + * @var float + */ + protected $delta = 0.0; + + /** + * @var int + */ + protected $maxDepth = 10; + + /** + * @var bool + */ + protected $canonicalize = false; + + /** + * @var bool + */ + protected $ignoreCase = false; + + /** + * @var SebastianBergmann\Comparator\ComparisonFailure + */ + protected $lastFailure; + + /** + * @param mixed $value + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @throws PHPUnit_Framework_Exception + */ + public function __construct($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + parent::__construct(); + + if (!is_numeric($delta)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'numeric'); + } + + if (!is_int($maxDepth)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'integer'); + } + + if (!is_bool($canonicalize)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean'); + } + + if (!is_bool($ignoreCase)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'boolean'); + } + + $this->value = $value; + $this->delta = $delta; + $this->maxDepth = $maxDepth; + $this->canonicalize = $canonicalize; + $this->ignoreCase = $ignoreCase; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + // If $this->value and $other are identical, they are also equal. + // This is the most common path and will allow us to skip + // initialization of all the comparators. + if ($this->value === $other) { + return true; + } + + $comparatorFactory = SebastianBergmann\Comparator\Factory::getInstance(); + + try { + $comparator = $comparatorFactory->getComparatorFor( + $this->value, + $other + ); + + $comparator->assertEquals( + $this->value, + $other, + $this->delta, + $this->canonicalize, + $this->ignoreCase + ); + } catch (SebastianBergmann\Comparator\ComparisonFailure $f) { + if ($returnResult) { + return false; + } + + throw new PHPUnit_Framework_ExpectationFailedException( + trim($description . "\n" . $f->getMessage()), + $f + ); + } + + return true; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $delta = ''; + + if (is_string($this->value)) { + if (strpos($this->value, "\n") !== false) { + return 'is equal to '; + } else { + return sprintf( + 'is equal to ', + $this->value + ); + } + } else { + if ($this->delta != 0) { + $delta = sprintf( + ' with delta <%F>', + $this->delta + ); + } + + return sprintf( + 'is equal to %s%s', + $this->exporter->export($this->value), + $delta + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Logical NOT. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_Not extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint + */ + protected $constraint; + + /** + * @param PHPUnit_Framework_Constraint $constraint + */ + public function __construct($constraint) + { + parent::__construct(); + + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + $constraint = new PHPUnit_Framework_Constraint_IsEqual($constraint); + } + + $this->constraint = $constraint; + } + + /** + * @param string $string + * + * @return string + */ + public static function negate($string) + { + return str_replace( + array( + 'contains ', + 'exists', + 'has ', + 'is ', + 'are ', + 'matches ', + 'starts with ', + 'ends with ', + 'reference ', + 'not not ' + ), + array( + 'does not contain ', + 'does not exist', + 'does not have ', + 'is not ', + 'are not ', + 'does not match ', + 'starts not with ', + 'ends not with ', + 'don\'t reference ', + 'not ' + ), + $string + ); + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = !$this->constraint->evaluate($other, $description, true); + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + switch (get_class($this->constraint)) { + case 'PHPUnit_Framework_Constraint_And': + case 'PHPUnit_Framework_Constraint_Not': + case 'PHPUnit_Framework_Constraint_Or': + return 'not( ' . $this->constraint->failureDescription($other) . ' )'; + + default: + return self::negate( + $this->constraint->failureDescription($other) + ); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + switch (get_class($this->constraint)) { + case 'PHPUnit_Framework_Constraint_And': + case 'PHPUnit_Framework_Constraint_Not': + case 'PHPUnit_Framework_Constraint_Or': + return 'not( ' . $this->constraint->toString() . ' )'; + + default: + return self::negate( + $this->constraint->toString() + ); + } + } + + /** + * Counts the number of constraint elements. + * + * @return int + * + * @since Method available since Release 3.4.0 + */ + public function count() + { + return count($this->constraint); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Logical AND. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_And extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint[] + */ + protected $constraints = array(); + + /** + * @var PHPUnit_Framework_Constraint + */ + protected $lastConstraint = null; + + /** + * @param PHPUnit_Framework_Constraint[] $constraints + * + * @throws PHPUnit_Framework_Exception + */ + public function setConstraints(array $constraints) + { + $this->constraints = array(); + + foreach ($constraints as $constraint) { + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + throw new PHPUnit_Framework_Exception( + 'All parameters to ' . __CLASS__ . + ' must be a constraint object.' + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = true; + $constraint = null; + + foreach ($this->constraints as $constraint) { + if (!$constraint->evaluate($other, $description, true)) { + $success = false; + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' and '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + * + * @return int + * + * @since Method available since Release 3.4.0 + */ + public function count() + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += count($constraint); + } + + return $count; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that asserts that the class it is evaluated for has a given + * static attribute. + * + * The attribute name is passed in the constructor. + * + * @since Class available since Release 3.1.0 + */ +class PHPUnit_Framework_Constraint_ClassHasStaticAttribute extends PHPUnit_Framework_Constraint_ClassHasAttribute +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + $class = new ReflectionClass($other); + + if ($class->hasProperty($this->attributeName)) { + $attribute = $class->getProperty($this->attributeName); + + return $attribute->isStatic(); + } else { + return false; + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + * + * @since Method available since Release 3.3.0 + */ + public function toString() + { + return sprintf( + 'has static attribute "%s"', + $this->attributeName + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Logical OR. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_Constraint_Or extends PHPUnit_Framework_Constraint +{ + /** + * @var PHPUnit_Framework_Constraint[] + */ + protected $constraints = array(); + + /** + * @param PHPUnit_Framework_Constraint[] $constraints + */ + public function setConstraints(array $constraints) + { + $this->constraints = array(); + + foreach ($constraints as $constraint) { + if (!($constraint instanceof PHPUnit_Framework_Constraint)) { + $constraint = new PHPUnit_Framework_Constraint_IsEqual( + $constraint + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other Value or object to evaluate. + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @return mixed + * + * @throws PHPUnit_Framework_ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = false; + $constraint = null; + + foreach ($this->constraints as $constraint) { + if ($constraint->evaluate($other, $description, true)) { + $success = true; + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' or '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + * + * @return int + * + * @since Method available since Release 3.4.0 + */ + public function count() + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += count($constraint); + } + + return $count; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 3.6.6 + */ +class PHPUnit_Framework_Constraint_Exception extends PHPUnit_Framework_Constraint +{ + /** + * @var string + */ + protected $className; + + /** + * @param string $className + */ + public function __construct($className) + { + parent::__construct(); + $this->className = $className; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return $other instanceof $this->className; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other Evaluated value or object. + * + * @return string + */ + protected function failureDescription($other) + { + if ($other !== null) { + $message = ''; + if ($other instanceof Exception) { + $message = '. Message was: "' . $other->getMessage() . '" at' + . "\n" . $other->getTraceAsString(); + } + + return sprintf( + 'exception of type "%s" matches expected exception "%s"%s', + get_class($other), + $this->className, + $message + ); + } + + return sprintf( + 'exception of type "%s" is thrown', + $this->className + ); + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return sprintf( + 'exception of type "%s"', + $this->className + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Constraint that accepts null. + * + * @since Class available since Release 3.3.0 + */ +class PHPUnit_Framework_Constraint_IsNull extends PHPUnit_Framework_Constraint +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other Value or object to evaluate. + * + * @return bool + */ + protected function matches($other) + { + return $other === null; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'is null'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Framework_CodeCoverageException extends PHPUnit_Framework_Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An incomplete test case + * + * @since Class available since Release 4.3.0 + */ +class PHPUnit_Framework_IncompleteTestCase extends PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + protected $message = ''; + + /** + * @var bool + */ + protected $backupGlobals = false; + + /** + * @var bool + */ + protected $backupStaticAttributes = false; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * @var bool + */ + protected $useErrorHandler = false; + + /** + * @var bool + */ + protected $useOutputBuffering = false; + + /** + * @param string $className + * @param string $methodName + * @param string $message + */ + public function __construct($className, $methodName, $message = '') + { + $this->message = $message; + parent::__construct($className . '::' . $methodName); + } + + /** + * @throws PHPUnit_Framework_Exception + */ + protected function runTest() + { + $this->markTestIncomplete($this->message); + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Returns a string representation of the test case. + * + * @return string + */ + public function toString() + { + return $this->getName(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Extension to PHPUnit_Framework_AssertionFailedError to mark the special + * case of an incomplete test. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Framework_IncompleteTestError extends PHPUnit_Framework_AssertionFailedError implements PHPUnit_Framework_IncompleteTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Wrapper for PHP errors. + * + * @since Class available since Release 2.2.0 + */ +class PHPUnit_Framework_Error extends PHPUnit_Framework_Exception +{ + /** + * Constructor. + * + * @param string $message + * @param int $code + * @param string $file + * @param int $line + * @param Exception $previous + */ + public function __construct($message, $code, $file, $line, Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->file = $file; + $this->line = $line; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Returns a matcher that matches when the method is executed + * zero or more times. + * + * @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount + * + * @since Method available since Release 3.0.0 + */ +function any() +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::any', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsAnything matcher object. + * + * @return PHPUnit_Framework_Constraint_IsAnything + * + * @since Method available since Release 3.0.0 + */ +function anything() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::anything', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ArrayHasKey matcher object. + * + * @param mixed $key + * + * @return PHPUnit_Framework_Constraint_ArrayHasKey + * + * @since Method available since Release 3.0.0 + */ +function arrayHasKey($key) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::arrayHasKey', + func_get_args() + ); +} + +/** + * Asserts that an array has a specified key. + * + * @param mixed $key + * @param array|ArrayAccess $array + * @param string $message + * + * @since Method available since Release 3.0.0 + */ +function assertArrayHasKey($key, $array, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertArrayHasKey', + func_get_args() + ); +} + +/** + * Asserts that an array has a specified subset. + * + * @param array|ArrayAccess $subset + * @param array|ArrayAccess $array + * @param bool $strict Check for object identity + * @param string $message + * + * @since Method available since Release 4.4.0 + */ +function assertArraySubset($subset, $array, $strict = false, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertArraySubset', + func_get_args() + ); +} + +/** + * Asserts that an array does not have a specified key. + * + * @param mixed $key + * @param array|ArrayAccess $array + * @param string $message + * + * @since Method available since Release 3.0.0 + */ +function assertArrayNotHasKey($key, $array, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertArrayNotHasKey', + func_get_args() + ); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 3.0.0 + */ +function assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeContains', + func_get_args() + ); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains only values of a given type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ +function assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeContainsOnly', + func_get_args() + ); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param int $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.6.0 + */ +function assertAttributeCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeCount', + func_get_args() + ); +} + +/** + * Asserts that a static attribute of a class or an attribute of an object + * is empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertAttributeEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeEmpty', + func_get_args() + ); +} + +/** + * Asserts that a variable is equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + */ +function assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeEquals', + func_get_args() + ); +} + +/** + * Asserts that an attribute is greater than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeGreaterThan', + func_get_args() + ); +} + +/** + * Asserts that an attribute is greater than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeGreaterThanOrEqual', + func_get_args() + ); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeInstanceOf', + func_get_args() + ); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeInternalType', + func_get_args() + ); +} + +/** + * Asserts that an attribute is smaller than another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeLessThan', + func_get_args() + ); +} + +/** + * Asserts that an attribute is smaller than or equal to another value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeLessThanOrEqual', + func_get_args() + ); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain a needle. + * + * @param mixed $needle + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 3.0.0 + */ +function assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotContains', + func_get_args() + ); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain only values of a given + * type. + * + * @param string $type + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ +function assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = null, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotContainsOnly', + func_get_args() + ); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param int $expectedCount + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.6.0 + */ +function assertAttributeNotCount($expectedCount, $haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotCount', + func_get_args() + ); +} + +/** + * Asserts that a static attribute of a class or an attribute of an object + * is not empty. + * + * @param string $haystackAttributeName + * @param mixed $haystackClassOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertAttributeNotEmpty($haystackAttributeName, $haystackClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotEmpty', + func_get_args() + ); +} + +/** + * Asserts that a variable is not equal to an attribute of an object. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param string $actualClassOrObject + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + */ +function assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotEquals', + func_get_args() + ); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotInstanceOf', + func_get_args() + ); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param string $expected + * @param string $attributeName + * @param mixed $classOrObject + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotInternalType', + func_get_args() + ); +} + +/** + * Asserts that a variable and an attribute of an object do not have the + * same type and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ +function assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeNotSame', + func_get_args() + ); +} + +/** + * Asserts that a variable and an attribute of an object have the same type + * and value. + * + * @param mixed $expected + * @param string $actualAttributeName + * @param object $actualClassOrObject + * @param string $message + */ +function assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertAttributeSame', + func_get_args() + ); +} + +/** + * Asserts that a class has a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertClassHasAttribute($attributeName, $className, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertClassHasAttribute', + func_get_args() + ); +} + +/** + * Asserts that a class has a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertClassHasStaticAttribute($attributeName, $className, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertClassHasStaticAttribute', + func_get_args() + ); +} + +/** + * Asserts that a class does not have a specified attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertClassNotHasAttribute($attributeName, $className, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertClassNotHasAttribute', + func_get_args() + ); +} + +/** + * Asserts that a class does not have a specified static attribute. + * + * @param string $attributeName + * @param string $className + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertClassNotHasStaticAttribute($attributeName, $className, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertClassNotHasStaticAttribute', + func_get_args() + ); +} + +/** + * Asserts that a haystack contains a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 2.1.0 + */ +function assertContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertContains', + func_get_args() + ); +} + +/** + * Asserts that a haystack contains only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ +function assertContainsOnly($type, $haystack, $isNativeType = null, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertContainsOnly', + func_get_args() + ); +} + +/** + * Asserts that a haystack contains only instances of a given classname + * + * @param string $classname + * @param array|Traversable $haystack + * @param string $message + */ +function assertContainsOnlyInstancesOf($classname, $haystack, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertContainsOnlyInstancesOf', + func_get_args() + ); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param int $expectedCount + * @param mixed $haystack + * @param string $message + */ +function assertCount($expectedCount, $haystack, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertCount', + func_get_args() + ); +} + +/** + * Asserts that a variable is empty. + * + * @param mixed $actual + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertEmpty($actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertEmpty', + func_get_args() + ); +} + +/** + * Asserts that a hierarchy of DOMElements matches. + * + * @param DOMElement $expectedElement + * @param DOMElement $actualElement + * @param bool $checkAttributes + * @param string $message + * + * @since Method available since Release 3.3.0 + */ +function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, $checkAttributes = false, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertEqualXMLStructure', + func_get_args() + ); +} + +/** + * Asserts that two variables are equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + */ +function assertEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertEquals', + func_get_args() + ); +} + +/** + * Asserts that a condition is not true. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertNotTrue($condition, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotTrue', + func_get_args() + ); +} + +/** + * Asserts that a condition is false. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertFalse($condition, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertFalse', + func_get_args() + ); +} + +/** + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.2.14 + */ +function assertFileEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertFileEquals', + func_get_args() + ); +} + +/** + * Asserts that a file exists. + * + * @param string $filename + * @param string $message + * + * @since Method available since Release 3.0.0 + */ +function assertFileExists($filename, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertFileExists', + func_get_args() + ); +} + +/** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @param string $expected + * @param string $actual + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.2.14 + */ +function assertFileNotEquals($expected, $actual, $message = '', $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertFileNotEquals', + func_get_args() + ); +} + +/** + * Asserts that a file does not exist. + * + * @param string $filename + * @param string $message + * + * @since Method available since Release 3.0.0 + */ +function assertFileNotExists($filename, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertFileNotExists', + func_get_args() + ); +} + +/** + * Asserts that a value is greater than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertGreaterThan($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertGreaterThan', + func_get_args() + ); +} + +/** + * Asserts that a value is greater than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertGreaterThanOrEqual($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertGreaterThanOrEqual', + func_get_args() + ); +} + +/** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertInstanceOf($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertInstanceOf', + func_get_args() + ); +} + +/** + * Asserts that a variable is of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertInternalType($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertInternalType', + func_get_args() + ); +} + +/** + * Asserts that a string is a valid JSON string. + * + * @param string $actualJson + * @param string $message + * + * @since Method available since Release 3.7.20 + */ +function assertJson($actualJson, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertJson', + func_get_args() + ); +} + +/** + * Asserts that two JSON files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + */ +function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertJsonFileEqualsJsonFile', + func_get_args() + ); +} + +/** + * Asserts that two JSON files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + */ +function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertJsonFileNotEqualsJsonFile', + func_get_args() + ); +} + +/** + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + */ +function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertJsonStringEqualsJsonFile', + func_get_args() + ); +} + +/** + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + */ +function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertJsonStringEqualsJsonString', + func_get_args() + ); +} + +/** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @param string $expectedFile + * @param string $actualJson + * @param string $message + */ +function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonFile', + func_get_args() + ); +} + +/** + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @param string $expectedJson + * @param string $actualJson + * @param string $message + */ +function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertJsonStringNotEqualsJsonString', + func_get_args() + ); +} + +/** + * Asserts that a value is smaller than another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertLessThan($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertLessThan', + func_get_args() + ); +} + +/** + * Asserts that a value is smaller than or equal to another value. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertLessThanOrEqual($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertLessThanOrEqual', + func_get_args() + ); +} + +/** + * Asserts that a haystack does not contain a needle. + * + * @param mixed $needle + * @param mixed $haystack + * @param string $message + * @param bool $ignoreCase + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @since Method available since Release 2.1.0 + */ +function assertNotContains($needle, $haystack, $message = '', $ignoreCase = false, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotContains', + func_get_args() + ); +} + +/** + * Asserts that a haystack does not contain only values of a given type. + * + * @param string $type + * @param mixed $haystack + * @param bool $isNativeType + * @param string $message + * + * @since Method available since Release 3.1.4 + */ +function assertNotContainsOnly($type, $haystack, $isNativeType = null, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotContainsOnly', + func_get_args() + ); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param int $expectedCount + * @param mixed $haystack + * @param string $message + */ +function assertNotCount($expectedCount, $haystack, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotCount', + func_get_args() + ); +} + +/** + * Asserts that a variable is not empty. + * + * @param mixed $actual + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertNotEmpty($actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotEmpty', + func_get_args() + ); +} + +/** + * Asserts that two variables are not equal. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 2.3.0 + */ +function assertNotEquals($expected, $actual, $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotEquals', + func_get_args() + ); +} + +/** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertNotInstanceOf($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotInstanceOf', + func_get_args() + ); +} + +/** + * Asserts that a variable is not of a given type. + * + * @param string $expected + * @param mixed $actual + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertNotInternalType($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotInternalType', + func_get_args() + ); +} + +/** + * Asserts that a condition is not false. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertNotFalse($condition, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotFalse', + func_get_args() + ); +} + +/** + * Asserts that a variable is not null. + * + * @param mixed $actual + * @param string $message + */ +function assertNotNull($actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotNull', + func_get_args() + ); +} + +/** + * Asserts that a string does not match a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + * + * @since Method available since Release 2.1.0 + */ +function assertNotRegExp($pattern, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotRegExp', + func_get_args() + ); +} + +/** + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ +function assertNotSame($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotSame', + func_get_args() + ); +} + +/** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is not the same. + * + * @param array|Countable|Traversable $expected + * @param array|Countable|Traversable $actual + * @param string $message + */ +function assertNotSameSize($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotSameSize', + func_get_args() + ); +} + +/** + * This assertion is the exact opposite of assertTag(). + * + * Rather than asserting that $matcher results in a match, it asserts that + * $matcher does not match. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + */ +function assertNotTag($matcher, $actual, $message = '', $isHtml = true) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNotTag', + func_get_args() + ); +} + +/** + * Asserts that a variable is null. + * + * @param mixed $actual + * @param string $message + */ +function assertNull($actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertNull', + func_get_args() + ); +} + +/** + * Asserts that an object has a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * + * @since Method available since Release 3.0.0 + */ +function assertObjectHasAttribute($attributeName, $object, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertObjectHasAttribute', + func_get_args() + ); +} + +/** + * Asserts that an object does not have a specified attribute. + * + * @param string $attributeName + * @param object $object + * @param string $message + * + * @since Method available since Release 3.0.0 + */ +function assertObjectNotHasAttribute($attributeName, $object, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertObjectNotHasAttribute', + func_get_args() + ); +} + +/** + * Asserts that a string matches a given regular expression. + * + * @param string $pattern + * @param string $string + * @param string $message + */ +function assertRegExp($pattern, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertRegExp', + func_get_args() + ); +} + +/** + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @param mixed $expected + * @param mixed $actual + * @param string $message + */ +function assertSame($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertSame', + func_get_args() + ); +} + +/** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. + * + * @param array|Countable|Traversable $expected + * @param array|Countable|Traversable $actual + * @param string $message + */ +function assertSameSize($expected, $actual, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertSameSize', + func_get_args() + ); +} + +/** + * Assert the presence, absence, or count of elements in a document matching + * the CSS $selector, regardless of the contents of those elements. + * + * The first argument, $selector, is the CSS selector used to match + * the elements in the $actual document. + * + * The second argument, $count, can be either boolean or numeric. + * When boolean, it asserts for presence of elements matching the selector + * (true) or absence of elements (false). + * When numeric, it asserts the count of elements. + * + * assertSelectCount("#binder", true, $xml); // any? + * assertSelectCount(".binder", 3, $xml); // exactly 3? + * + * @param array $selector + * @param int $count + * @param mixed $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + */ +function assertSelectCount($selector, $count, $actual, $message = '', $isHtml = true) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertSelectCount', + func_get_args() + ); +} + +/** + * assertSelectEquals("#binder .name", "Chuck", true, $xml); // any? + * assertSelectEquals("#binder .name", "Chuck", false, $xml); // none? + * + * @param array $selector + * @param string $content + * @param int $count + * @param mixed $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + */ +function assertSelectEquals($selector, $content, $count, $actual, $message = '', $isHtml = true) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertSelectEquals', + func_get_args() + ); +} + +/** + * assertSelectRegExp("#binder .name", "/Mike|Derek/", true, $xml); // any? + * assertSelectRegExp("#binder .name", "/Mike|Derek/", 3, $xml);// 3? + * + * @param array $selector + * @param string $pattern + * @param int $count + * @param mixed $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + */ +function assertSelectRegExp($selector, $pattern, $count, $actual, $message = '', $isHtml = true) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertSelectRegExp', + func_get_args() + ); +} + +/** + * Asserts that a string ends not with a given prefix. + * + * @param string $suffix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ +function assertStringEndsNotWith($suffix, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringEndsNotWith', + func_get_args() + ); +} + +/** + * Asserts that a string ends with a given prefix. + * + * @param string $suffix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ +function assertStringEndsWith($suffix, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringEndsWith', + func_get_args() + ); +} + +/** + * Asserts that the contents of a string is equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.3.0 + */ +function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringEqualsFile', + func_get_args() + ); +} + +/** + * Asserts that a string matches a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertStringMatchesFormat($format, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringMatchesFormat', + func_get_args() + ); +} + +/** + * Asserts that a string matches a given format file. + * + * @param string $formatFile + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertStringMatchesFormatFile($formatFile, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringMatchesFormatFile', + func_get_args() + ); +} + +/** + * Asserts that the contents of a string is not equal + * to the contents of a file. + * + * @param string $expectedFile + * @param string $actualString + * @param string $message + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @since Method available since Release 3.3.0 + */ +function assertStringNotEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringNotEqualsFile', + func_get_args() + ); +} + +/** + * Asserts that a string does not match a given format string. + * + * @param string $format + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertStringNotMatchesFormat($format, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringNotMatchesFormat', + func_get_args() + ); +} + +/** + * Asserts that a string does not match a given format string. + * + * @param string $formatFile + * @param string $string + * @param string $message + * + * @since Method available since Release 3.5.0 + */ +function assertStringNotMatchesFormatFile($formatFile, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringNotMatchesFormatFile', + func_get_args() + ); +} + +/** + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ +function assertStringStartsNotWith($prefix, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringStartsNotWith', + func_get_args() + ); +} + +/** + * Asserts that a string starts with a given prefix. + * + * @param string $prefix + * @param string $string + * @param string $message + * + * @since Method available since Release 3.4.0 + */ +function assertStringStartsWith($prefix, $string, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertStringStartsWith', + func_get_args() + ); +} + +/** + * Evaluate an HTML or XML string and assert its structure and/or contents. + * + * The first argument ($matcher) is an associative array that specifies the + * match criteria for the assertion: + * + * - `id` : the node with the given id attribute must match the + * corresponding value. + * - `tag` : the node type must match the corresponding value. + * - `attributes` : a hash. The node's attributes must match the + * corresponding values in the hash. + * - `content` : The text content must match the given value. + * - `parent` : a hash. The node's parent must match the + * corresponding hash. + * - `child`: a hash. At least one of the node's immediate children + * must meet the criteria described by the hash. + * - `ancestor` : a hash. At least one of the node's ancestors must + * meet the criteria described by the hash. + * - `descendant` : a hash. At least one of the node's descendants must + * meet the criteria described by the hash. + * - `children` : a hash, for counting children of a node. + * Accepts the keys: + *- `count`: a number which must equal the number of children + * that match + *- `less_than`: the number of matching children must be greater + * than this number + *- `greater_than` : the number of matching children must be less than + * this number + *- `only` : another hash consisting of the keys to use to match + * on the children, and only matching children will be + * counted + * + * + * // Matcher that asserts that there is an element with an id="my_id". + * $matcher = array('id' => 'my_id'); + * + * // Matcher that asserts that there is a "span" tag. + * $matcher = array('tag' => 'span'); + * + * // Matcher that asserts that there is a "span" tag with the content + * // "Hello World". + * $matcher = array('tag' => 'span', 'content' => 'Hello World'); + * + * // Matcher that asserts that there is a "span" tag with content matching + * // the regular expression pattern. + * $matcher = array('tag' => 'span', 'content' => 'regexp:/Try P(HP|ython)/'); + * + * // Matcher that asserts that there is a "span" with an "list" class + * // attribute. + * $matcher = array( + * 'tag'=> 'span', + * 'attributes' => array('class' => 'list') + * ); + * + * // Matcher that asserts that there is a "span" inside of a "div". + * $matcher = array( + * 'tag'=> 'span', + * 'parent' => array('tag' => 'div') + * ); + * + * // Matcher that asserts that there is a "span" somewhere inside a + * // "table". + * $matcher = array( + * 'tag' => 'span', + * 'ancestor' => array('tag' => 'table') + * ); + * + * // Matcher that asserts that there is a "span" with at least one "em" + * // child. + * $matcher = array( + * 'tag' => 'span', + * 'child' => array('tag' => 'em') + * ); + * + * // Matcher that asserts that there is a "span" containing a (possibly + * // nested) "strong" tag. + * $matcher = array( + * 'tag'=> 'span', + * 'descendant' => array('tag' => 'strong') + * ); + * + * // Matcher that asserts that there is a "span" containing 5-10 "em" tags + * // as immediate children. + * $matcher = array( + * 'tag' => 'span', + * 'children' => array( + * 'less_than'=> 11, + * 'greater_than' => 4, + * 'only' => array('tag' => 'em') + * ) + * ); + * + * // Matcher that asserts that there is a "div", with an "ul" ancestor and + * // a "li" parent (with class="enum"), and containing a "span" descendant + * // that contains an element with id="my_test" and the text "Hello World". + * $matcher = array( + * 'tag'=> 'div', + * 'ancestor' => array('tag' => 'ul'), + * 'parent' => array( + * 'tag'=> 'li', + * 'attributes' => array('class' => 'enum') + * ), + * 'descendant' => array( + * 'tag' => 'span', + * 'child' => array( + * 'id' => 'my_test', + * 'content' => 'Hello World' + * ) + * ) + * ); + * + * // Use assertTag() to apply a $matcher to a piece of $html. + * $this->assertTag($matcher, $html); + * + * // Use assertTag() to apply a $matcher to a piece of $xml. + * $this->assertTag($matcher, $xml, '', false); + * + * + * The second argument ($actual) is a string containing either HTML or + * XML text to be tested. + * + * The third argument ($message) is an optional message that will be + * used if the assertion fails. + * + * The fourth argument ($html) is an optional flag specifying whether + * to load the $actual string into a DOMDocument using the HTML or + * XML load strategy. It is true by default, which assumes the HTML + * load strategy. In many cases, this will be acceptable for XML as well. + * + * @param array $matcher + * @param string $actual + * @param string $message + * @param bool $isHtml + * + * @since Method available since Release 3.3.0 + */ +function assertTag($matcher, $actual, $message = '', $isHtml = true) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertTag', + func_get_args() + ); +} + +/** + * Evaluates a PHPUnit_Framework_Constraint matcher object. + * + * @param mixed$value + * @param PHPUnit_Framework_Constraint $constraint + * @param string $message + * + * @since Method available since Release 3.0.0 + */ +function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertThat', + func_get_args() + ); +} + +/** + * Asserts that a condition is true. + * + * @param bool $condition + * @param string $message + * + * @throws PHPUnit_Framework_AssertionFailedError + */ +function assertTrue($condition, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertTrue', + func_get_args() + ); +} + +/** + * Asserts that two XML files are equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertXmlFileEqualsXmlFile($expectedFile, $actualFile, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertXmlFileEqualsXmlFile', + func_get_args() + ); +} + +/** + * Asserts that two XML files are not equal. + * + * @param string $expectedFile + * @param string $actualFile + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertXmlFileNotEqualsXmlFile($expectedFile, $actualFile, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertXmlFileNotEqualsXmlFile', + func_get_args() + ); +} + +/** + * Asserts that two XML documents are equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.3.0 + */ +function assertXmlStringEqualsXmlFile($expectedFile, $actualXml, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertXmlStringEqualsXmlFile', + func_get_args() + ); +} + +/** + * Asserts that two XML documents are equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertXmlStringEqualsXmlString($expectedXml, $actualXml, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertXmlStringEqualsXmlString', + func_get_args() + ); +} + +/** + * Asserts that two XML documents are not equal. + * + * @param string $expectedFile + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.3.0 + */ +function assertXmlStringNotEqualsXmlFile($expectedFile, $actualXml, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlFile', + func_get_args() + ); +} + +/** + * Asserts that two XML documents are not equal. + * + * @param string $expectedXml + * @param string $actualXml + * @param string $message + * + * @since Method available since Release 3.1.0 + */ +function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, $message = '') +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlString', + func_get_args() + ); +} + +/** + * Returns a matcher that matches when the method is executed + * at the given $index. + * + * @param int $index + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex + * + * @since Method available since Release 3.0.0 + */ +function at($index) +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::at', + func_get_args() + ); +} + +/** + * Returns a matcher that matches when the method is executed at least once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce + * + * @since Method available since Release 3.0.0 + */ +function atLeastOnce() +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::atLeastOnce', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Attribute matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_Attribute + * + * @since Method available since Release 3.1.0 + */ +function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::attribute', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object + * that is wrapped in a PHPUnit_Framework_Constraint_Attribute matcher + * object. + * + * @param string $attributeName + * @param mixed $value + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @return PHPUnit_Framework_Constraint_Attribute + * + * @since Method available since Release 3.1.0 + */ +function attributeEqualTo($attributeName, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::attributeEqualTo', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Callback matcher object. + * + * @param callable $callback + * + * @return PHPUnit_Framework_Constraint_Callback + */ +function callback($callback) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::callback', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ClassHasAttribute matcher object. + * + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_ClassHasAttribute + * + * @since Method available since Release 3.1.0 + */ +function classHasAttribute($attributeName) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::classHasAttribute', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ClassHasStaticAttribute matcher + * object. + * + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_ClassHasStaticAttribute + * + * @since Method available since Release 3.1.0 + */ +function classHasStaticAttribute($attributeName) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::classHasStaticAttribute', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_TraversableContains matcher + * object. + * + * @param mixed $value + * @param bool $checkForObjectIdentity + * @param bool $checkForNonObjectIdentity + * + * @return PHPUnit_Framework_Constraint_TraversableContains + * + * @since Method available since Release 3.0.0 + */ +function contains($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::contains', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher + * object. + * + * @param string $type + * + * @return PHPUnit_Framework_Constraint_TraversableContainsOnly + * + * @since Method available since Release 3.1.4 + */ +function containsOnly($type) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::containsOnly', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_TraversableContainsOnly matcher + * object. + * + * @param string $classname + * + * @return PHPUnit_Framework_Constraint_TraversableContainsOnly + */ +function containsOnlyInstancesOf($classname) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::containsOnlyInstancesOf', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsEqual matcher object. + * + * @param mixed $value + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @return PHPUnit_Framework_Constraint_IsEqual + * + * @since Method available since Release 3.0.0 + */ +function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::equalTo', + func_get_args() + ); +} + +/** + * Returns a matcher that matches when the method is executed + * exactly $count times. + * + * @param int $count + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * + * @since Method available since Release 3.0.0 + */ +function exactly($count) +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::exactly', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_FileExists matcher object. + * + * @return PHPUnit_Framework_Constraint_FileExists + * + * @since Method available since Release 3.0.0 + */ +function fileExists() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::fileExists', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_GreaterThan + * + * @since Method available since Release 3.0.0 + */ +function greaterThan($value) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::greaterThan', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_GreaterThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_Or + * + * @since Method available since Release 3.1.0 + */ +function greaterThanOrEqual($value) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::greaterThanOrEqual', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsIdentical matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_IsIdentical + * + * @since Method available since Release 3.0.0 + */ +function identicalTo($value) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::identicalTo', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsEmpty matcher object. + * + * @return PHPUnit_Framework_Constraint_IsEmpty + * + * @since Method available since Release 3.5.0 + */ +function isEmpty() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::isEmpty', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsFalse matcher object. + * + * @return PHPUnit_Framework_Constraint_IsFalse + * + * @since Method available since Release 3.3.0 + */ +function isFalse() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::isFalse', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsInstanceOf matcher object. + * + * @param string $className + * + * @return PHPUnit_Framework_Constraint_IsInstanceOf + * + * @since Method available since Release 3.0.0 + */ +function isInstanceOf($className) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::isInstanceOf', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsJson matcher object. + * + * @return PHPUnit_Framework_Constraint_IsJson + * + * @since Method available since Release 3.7.20 + */ +function isJson() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::isJson', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsNull matcher object. + * + * @return PHPUnit_Framework_Constraint_IsNull + * + * @since Method available since Release 3.3.0 + */ +function isNull() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::isNull', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsTrue matcher object. + * + * @return PHPUnit_Framework_Constraint_IsTrue + * + * @since Method available since Release 3.3.0 + */ +function isTrue() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::isTrue', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_IsType matcher object. + * + * @param string $type + * + * @return PHPUnit_Framework_Constraint_IsType + * + * @since Method available since Release 3.0.0 + */ +function isType($type) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::isType', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_LessThan + * + * @since Method available since Release 3.0.0 + */ +function lessThan($value) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::lessThan', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Or matcher object that wraps + * a PHPUnit_Framework_Constraint_IsEqual and a + * PHPUnit_Framework_Constraint_LessThan matcher object. + * + * @param mixed $value + * + * @return PHPUnit_Framework_Constraint_Or + * + * @since Method available since Release 3.1.0 + */ +function lessThanOrEqual($value) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::lessThanOrEqual', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_And matcher object. + * + * @return PHPUnit_Framework_Constraint_And + * + * @since Method available since Release 3.0.0 + */ +function logicalAnd() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::logicalAnd', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Not matcher object. + * + * @param PHPUnit_Framework_Constraint $constraint + * + * @return PHPUnit_Framework_Constraint_Not + * + * @since Method available since Release 3.0.0 + */ +function logicalNot(PHPUnit_Framework_Constraint $constraint) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::logicalNot', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Or matcher object. + * + * @return PHPUnit_Framework_Constraint_Or + * + * @since Method available since Release 3.0.0 + */ +function logicalOr() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::logicalOr', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_Xor matcher object. + * + * @return PHPUnit_Framework_Constraint_Xor + * + * @since Method available since Release 3.0.0 + */ +function logicalXor() +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::logicalXor', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringMatches matcher object. + * + * @param string $string + * + * @return PHPUnit_Framework_Constraint_StringMatches + * + * @since Method available since Release 3.5.0 + */ +function matches($string) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::matches', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_PCREMatch matcher object. + * + * @param string $pattern + * + * @return PHPUnit_Framework_Constraint_PCREMatch + * + * @since Method available since Release 3.0.0 + */ +function matchesRegularExpression($pattern) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::matchesRegularExpression', + func_get_args() + ); +} + +/** + * Returns a matcher that matches when the method is never executed. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * + * @since Method available since Release 3.0.0 + */ +function never() +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::never', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_ObjectHasAttribute matcher object. + * + * @param string $attributeName + * + * @return PHPUnit_Framework_Constraint_ObjectHasAttribute + * + * @since Method available since Release 3.0.0 + */ +function objectHasAttribute($attributeName) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::objectHasAttribute', + func_get_args() + ); +} + +/** + * @param mixed $value, ... + * + * @return PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls + * + * @since Method available since Release 3.0.0 + */ +function onConsecutiveCalls() +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::onConsecutiveCalls', + func_get_args() + ); +} + +/** + * Returns a matcher that matches when the method is executed exactly once. + * + * @return PHPUnit_Framework_MockObject_Matcher_InvokedCount + * + * @since Method available since Release 3.0.0 + */ +function once() +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::once', + func_get_args() + ); +} + +/** + * @param int $argumentIndex + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnArgument + * + * @since Method available since Release 3.3.0 + */ +function returnArgument($argumentIndex) +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::returnArgument', + func_get_args() + ); +} + +/** + * @param mixed $callback + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnCallback + * + * @since Method available since Release 3.3.0 + */ +function returnCallback($callback) +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::returnCallback', + func_get_args() + ); +} + +/** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnSelf + * + * @since Method available since Release 3.6.0 + */ +function returnSelf() +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::returnSelf', + func_get_args() + ); +} + +/** + * @param mixed $value + * + * @return PHPUnit_Framework_MockObject_Stub_Return + * + * @since Method available since Release 3.0.0 + */ +function returnValue($value) +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::returnValue', + func_get_args() + ); +} + +/** + * @param array $valueMap + * + * @return PHPUnit_Framework_MockObject_Stub_ReturnValueMap + * + * @since Method available since Release 3.6.0 + */ +function returnValueMap(array $valueMap) +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::returnValueMap', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringContains matcher object. + * + * @param string $string + * @param bool $case + * + * @return PHPUnit_Framework_Constraint_StringContains + * + * @since Method available since Release 3.0.0 + */ +function stringContains($string, $case = true) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::stringContains', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringEndsWith matcher object. + * + * @param mixed $suffix + * + * @return PHPUnit_Framework_Constraint_StringEndsWith + * + * @since Method available since Release 3.4.0 + */ +function stringEndsWith($suffix) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::stringEndsWith', + func_get_args() + ); +} + +/** + * Returns a PHPUnit_Framework_Constraint_StringStartsWith matcher object. + * + * @param mixed $prefix + * + * @return PHPUnit_Framework_Constraint_StringStartsWith + * + * @since Method available since Release 3.4.0 + */ +function stringStartsWith($prefix) +{ + return call_user_func_array( + 'PHPUnit_Framework_Assert::stringStartsWith', + func_get_args() + ); +} + +/** + * @param Exception $exception + * + * @return PHPUnit_Framework_MockObject_Stub_Exception + * + * @since Method available since Release 3.1.0 + */ +function throwException(Exception $exception) +{ + return call_user_func_array( + 'PHPUnit_Framework_TestCase::throwException', + func_get_args() + ); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A marker interface for marking any exception/error as result of an unit + * test as incomplete implementation or currently not implemented. + * + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Framework_IncompleteTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Exception for expectations which failed their check. + * + * The exception contains the error message and optionally a + * SebastianBergmann\Comparator\ComparisonFailure which is used to + * generate diff output of the failed expectations. + * + * @since Class available since Release 3.0.0 + */ +class PHPUnit_Framework_ExpectationFailedException extends PHPUnit_Framework_AssertionFailedError +{ + /** + * @var SebastianBergmann\Comparator\ComparisonFailure + */ + protected $comparisonFailure; + + public function __construct($message, SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null, Exception $previous = null) + { + $this->comparisonFailure = $comparisonFailure; + + parent::__construct($message, 0, $previous); + } + + /** + * @return SebastianBergmann\Comparator\ComparisonFailure + */ + public function getComparisonFailure() + { + return $this->comparisonFailure; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for all PHPUnit Framework exceptions. + * + * Ensures that exceptions thrown during a test run do not leave stray + * references behind. + * + * Every Exception contains a stack trace. Each stack frame contains the 'args' + * of the called function. The function arguments can contain references to + * instantiated objects. The references prevent the objects from being + * destructed (until test results are eventually printed), so memory cannot be + * freed up. + * + * With enabled process isolation, test results are serialized in the child + * process and unserialized in the parent process. The stack trace of Exceptions + * may contain objects that cannot be serialized or unserialized (e.g., PDO + * connections). Unserializing user-space objects from the child process into + * the parent would break the intended encapsulation of process isolation. + * + * @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions + * @since Class available since Release 3.4.0 + */ +class PHPUnit_Framework_Exception extends RuntimeException implements PHPUnit_Exception +{ + /** + * @var array + */ + protected $serializableTrace; + + public function __construct($message = '', $code = 0, Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->serializableTrace = $this->getTrace(); + foreach ($this->serializableTrace as $i => $call) { + unset($this->serializableTrace[$i]['args']); + } + } + + /** + * Returns the serializable trace (without 'args'). + * + * @return array + */ + public function getSerializableTrace() + { + return $this->serializableTrace; + } + + /** + * @return string + */ + public function __toString() + { + $string = PHPUnit_Framework_TestFailure::exceptionToString($this); + + if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; + } + + return $string; + } + + public function __sleep() + { + return array_keys(get_object_vars($this)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A skipped test case + * + * @since Class available since Release 4.3.0 + */ +class PHPUnit_Framework_SkippedTestCase extends PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + protected $message = ''; + + /** + * @var bool + */ + protected $backupGlobals = false; + + /** + * @var bool + */ + protected $backupStaticAttributes = false; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * @var bool + */ + protected $useErrorHandler = false; + + /** + * @var bool + */ + protected $useOutputBuffering = false; + + /** + * @param string $message + */ + public function __construct($className, $methodName, $message = '') + { + $this->message = $message; + parent::__construct($className . '::' . $methodName); + } + + /** + * @throws PHPUnit_Framework_Exception + */ + protected function runTest() + { + $this->markTestSkipped($this->message); + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Returns a string representation of the test case. + * + * @return string + */ + public function toString() + { + return $this->getName(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This class defines the current version of PHPUnit. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Runner_Version +{ + private static $pharVersion = "4.8.26"; + private static $version; + + /** + * Returns the current version of PHPUnit. + * + * @return string + */ + public static function id() + { + if (self::$pharVersion !== null) { + return self::$pharVersion; + } + + if (self::$version === null) { + $version = new SebastianBergmann\Version('4.8.26', dirname(dirname(__DIR__))); + self::$version = $version->getVersion(); + } + + return self::$version; + } + + /** + * @return string + * + * @since Method available since Release 4.8.13 + */ + public static function series() + { + if (strpos(self::id(), '-')) { + $tmp = explode('-', self::id()); + $version = $tmp[0]; + } else { + $version = self::id(); + } + + return implode('.', array_slice(explode('.', $version), 0, 2)); + } + + /** + * @return string + */ + public static function getVersionString() + { + return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; + } + + /** + * @return string + * + * @since Method available since Release 4.0.0 + */ + public static function getReleaseChannel() + { + if (strpos(self::$pharVersion, '-') !== false) { + return '-nightly'; + } + + return ''; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for all test runners. + * + * @since Class available since Release 2.0.0 + */ +abstract class PHPUnit_Runner_BaseTestRunner +{ + const STATUS_PASSED = 0; + const STATUS_SKIPPED = 1; + const STATUS_INCOMPLETE = 2; + const STATUS_FAILURE = 3; + const STATUS_ERROR = 4; + const STATUS_RISKY = 5; + const SUITE_METHODNAME = 'suite'; + + /** + * Returns the loader to be used. + * + * @return PHPUnit_Runner_TestSuiteLoader + */ + public function getLoader() + { + return new PHPUnit_Runner_StandardTestSuiteLoader; + } + + /** + * Returns the Test corresponding to the given suite. + * This is a template method, subclasses override + * the runFailed() and clearStatus() methods. + * + * @param string $suiteClassName + * @param string $suiteClassFile + * @param mixed $suffixes + * + * @return PHPUnit_Framework_Test + */ + public function getTest($suiteClassName, $suiteClassFile = '', $suffixes = '') + { + if (is_dir($suiteClassName) && + !is_file($suiteClassName . '.php') && empty($suiteClassFile)) { + $facade = new File_Iterator_Facade; + $files = $facade->getFilesAsArray( + $suiteClassName, + $suffixes + ); + + $suite = new PHPUnit_Framework_TestSuite($suiteClassName); + $suite->addTestFiles($files); + + return $suite; + } + + try { + $testClass = $this->loadSuiteClass( + $suiteClassName, + $suiteClassFile + ); + } catch (PHPUnit_Framework_Exception $e) { + $this->runFailed($e->getMessage()); + + return; + } + + try { + $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME); + + if (!$suiteMethod->isStatic()) { + $this->runFailed( + 'suite() method must be static.' + ); + + return; + } + + try { + $test = $suiteMethod->invoke(null, $testClass->getName()); + } catch (ReflectionException $e) { + $this->runFailed( + sprintf( + "Failed to invoke suite() method.\n%s", + $e->getMessage() + ) + ); + + return; + } + } catch (ReflectionException $e) { + try { + $test = new PHPUnit_Framework_TestSuite($testClass); + } catch (PHPUnit_Framework_Exception $e) { + $test = new PHPUnit_Framework_TestSuite; + $test->setName($suiteClassName); + } + } + + $this->clearStatus(); + + return $test; + } + + /** + * Returns the loaded ReflectionClass for a suite name. + * + * @param string $suiteClassName + * @param string $suiteClassFile + * + * @return ReflectionClass + */ + protected function loadSuiteClass($suiteClassName, $suiteClassFile = '') + { + $loader = $this->getLoader(); + + return $loader->load($suiteClassName, $suiteClassFile); + } + + /** + * Clears the status message. + */ + protected function clearStatus() + { + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + * + * @param string $message + */ + abstract protected function runFailed($message); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Runner_Filter_Group_Exclude extends PHPUnit_Runner_Filter_GroupFilterIterator +{ + protected function doAccept($hash) + { + return !in_array($hash, $this->groupTests); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Runner_Filter_Group_Include extends PHPUnit_Runner_Filter_GroupFilterIterator +{ + protected function doAccept($hash) + { + return in_array($hash, $this->groupTests); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Runner_Filter_Test extends RecursiveFilterIterator +{ + /** + * @var string + */ + protected $filter = null; + + /** + * @var int + */ + protected $filterMin; + /** + * @var int + */ + protected $filterMax; + + /** + * @param RecursiveIterator $iterator + * @param string $filter + */ + public function __construct(RecursiveIterator $iterator, $filter) + { + parent::__construct($iterator); + $this->setFilter($filter); + } + + /** + * @param string $filter + */ + protected function setFilter($filter) + { + if (PHPUnit_Util_Regex::pregMatchSafe($filter, '') === false) { + // Handles: + // * testAssertEqualsSucceeds#4 + // * testAssertEqualsSucceeds#4-8 + if (preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) { + if (isset($matches[3]) && $matches[2] < $matches[3]) { + $filter = sprintf( + '%s.*with data set #(\d+)$', + $matches[1] + ); + + $this->filterMin = $matches[2]; + $this->filterMax = $matches[3]; + } else { + $filter = sprintf( + '%s.*with data set #%s$', + $matches[1], + $matches[2] + ); + } + } // Handles: + // * testDetermineJsonError@JSON_ERROR_NONE + // * testDetermineJsonError@JSON.* + elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) { + $filter = sprintf( + '%s.*with data set "%s"$', + $matches[1], + $matches[2] + ); + } + + // Escape delimiters in regular expression. Do NOT use preg_quote, + // to keep magic characters. + $filter = sprintf('/%s/', str_replace( + '/', + '\\/', + $filter + )); + } + + $this->filter = $filter; + } + + /** + * @return bool + */ + public function accept() + { + $test = $this->getInnerIterator()->current(); + + if ($test instanceof PHPUnit_Framework_TestSuite) { + return true; + } + + $tmp = PHPUnit_Util_Test::describe($test, false); + + if ($tmp[0] != '') { + $name = implode('::', $tmp); + } else { + $name = $tmp[1]; + } + + $accepted = preg_match($this->filter, $name, $matches); + + if ($accepted && isset($this->filterMax)) { + $set = end($matches); + $accepted = $set >= $this->filterMin && $set <= $this->filterMax; + } + + return $accepted; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Runner_Filter_Factory +{ + /** + * @var array + */ + private $filters = array(); + + /** + * @param ReflectionClass $filter + * @param mixed $args + */ + public function addFilter(ReflectionClass $filter, $args) + { + if (!$filter->isSubclassOf('RecursiveFilterIterator')) { + throw new InvalidArgumentException( + sprintf( + 'Class "%s" does not extend RecursiveFilterIterator', + $filter->name + ) + ); + } + + $this->filters[] = array($filter, $args); + } + + /** + * @return FilterIterator + */ + public function factory(Iterator $iterator, PHPUnit_Framework_TestSuite $suite) + { + foreach ($this->filters as $filter) { + list($class, $args) = $filter; + $iterator = $class->newInstance($iterator, $args, $suite); + } + + return $iterator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +abstract class PHPUnit_Runner_Filter_GroupFilterIterator extends RecursiveFilterIterator +{ + /** + * @var array + */ + protected $groupTests = array(); + + /** + * @param RecursiveIterator $iterator + * @param array $groups + * @param PHPUnit_Framework_TestSuite $suite + */ + public function __construct(RecursiveIterator $iterator, array $groups, PHPUnit_Framework_TestSuite $suite) + { + parent::__construct($iterator); + + foreach ($suite->getGroupDetails() as $group => $tests) { + if (in_array($group, $groups)) { + $testHashes = array_map( + function ($test) { + return spl_object_hash($test); + }, + $tests + ); + + $this->groupTests = array_merge($this->groupTests, $testHashes); + } + } + } + + /** + * @return bool + */ + public function accept() + { + $test = $this->getInnerIterator()->current(); + + if ($test instanceof PHPUnit_Framework_TestSuite) { + return true; + } + + return $this->doAccept(spl_object_hash($test)); + } + + abstract protected function doAccept($hash); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * An interface to define how a test suite should be loaded. + * + * @since Interface available since Release 2.0.0 + */ +interface PHPUnit_Runner_TestSuiteLoader +{ + /** + * @param string $suiteClassName + * @param string $suiteClassFile + * + * @return ReflectionClass + */ + public function load($suiteClassName, $suiteClassFile = ''); + + /** + * @param ReflectionClass $aClass + * + * @return ReflectionClass + */ + public function reload(ReflectionClass $aClass); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The standard test suite loader. + * + * @since Class available since Release 2.0.0 + */ +class PHPUnit_Runner_StandardTestSuiteLoader implements PHPUnit_Runner_TestSuiteLoader +{ + /** + * @param string $suiteClassName + * @param string $suiteClassFile + * + * @return ReflectionClass + * + * @throws PHPUnit_Framework_Exception + */ + public function load($suiteClassName, $suiteClassFile = '') + { + $suiteClassName = str_replace('.php', '', $suiteClassName); + + if (empty($suiteClassFile)) { + $suiteClassFile = PHPUnit_Util_Filesystem::classNameToFilename( + $suiteClassName + ); + } + + if (!class_exists($suiteClassName, false)) { + $loadedClasses = get_declared_classes(); + + $filename = PHPUnit_Util_Fileloader::checkAndLoad($suiteClassFile); + + $loadedClasses = array_values( + array_diff(get_declared_classes(), $loadedClasses) + ); + } + + if (!class_exists($suiteClassName, false) && !empty($loadedClasses)) { + $offset = 0 - strlen($suiteClassName); + + foreach ($loadedClasses as $loadedClass) { + $class = new ReflectionClass($loadedClass); + if (substr($loadedClass, $offset) === $suiteClassName && + $class->getFileName() == $filename) { + $suiteClassName = $loadedClass; + break; + } + } + } + + if (!class_exists($suiteClassName, false) && !empty($loadedClasses)) { + $testCaseClass = 'PHPUnit_Framework_TestCase'; + + foreach ($loadedClasses as $loadedClass) { + $class = new ReflectionClass($loadedClass); + $classFile = $class->getFileName(); + + if ($class->isSubclassOf($testCaseClass) && + !$class->isAbstract()) { + $suiteClassName = $loadedClass; + $testCaseClass = $loadedClass; + + if ($classFile == realpath($suiteClassFile)) { + break; + } + } + + if ($class->hasMethod('suite')) { + $method = $class->getMethod('suite'); + + if (!$method->isAbstract() && + $method->isPublic() && + $method->isStatic()) { + $suiteClassName = $loadedClass; + + if ($classFile == realpath($suiteClassFile)) { + break; + } + } + } + } + } + + if (class_exists($suiteClassName, false)) { + $class = new ReflectionClass($suiteClassName); + + if ($class->getFileName() == realpath($suiteClassFile)) { + return $class; + } + } + + throw new PHPUnit_Framework_Exception( + sprintf( + "Class '%s' could not be found in '%s'.", + $suiteClassName, + $suiteClassFile + ) + ); + } + + /** + * @param ReflectionClass $aClass + * + * @return ReflectionClass + */ + public function reload(ReflectionClass $aClass) + { + return $aClass; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @since Class available since Release 4.0.0 + */ +class PHPUnit_Runner_Exception extends RuntimeException implements PHPUnit_Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Marker interface for PHPUnit exceptions. + * + * @since Interface available since Release 4.0.0 + */ +interface PHPUnit_Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann; + +/** + * @since Class available since Release 1.0.0 + */ +class Version +{ + private $path; + private $release; + private $version; + + /** + * @param string $release + * @param string $path + */ + public function __construct($release, $path) + { + $this->release = $release; + $this->path = $path; + } + + /** + * @return string + */ + public function getVersion() + { + if ($this->version === null) { + if (count(explode('.', $this->release)) == 3) { + $this->version = $this->release; + } else { + $this->version = $this->release . '-dev'; + } + + $git = $this->getGitInformation($this->path); + + if ($git) { + if (count(explode('.', $this->release)) == 3) { + $this->version = $git; + } else { + $git = explode('-', $git); + + $this->version = $this->release . '-' . end($git); + } + } + } + + return $this->version; + } + + /** + * @param string $path + * @return bool|string + */ + private function getGitInformation($path) + { + if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) { + return false; + } + + $dir = getcwd(); + chdir($path); + $returnCode = 1; + $result = @exec('git describe --tags 2>&1', $output, $returnCode); + chdir($dir); + + if ($returnCode !== 0) { + return false; + } + + return $result; + } +} +Version + +Copyright (c) 2013-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use SebastianBergmann\Comparator\Factory as BaseFactory; + +/** + * Prophecy comparator factory. + * + * @author Konstantin Kudryashov + */ +final class Factory extends BaseFactory +{ + /** + * @var Factory + */ + private static $instance; + + public function __construct() + { + parent::__construct(); + + $this->register(new ClosureComparator()); + $this->register(new ProphecyComparator()); + } + + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new Factory; + } + + return self::$instance; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use Prophecy\Prophecy\ProphecyInterface; +use SebastianBergmann\Comparator\ObjectComparator; + +class ProphecyComparator extends ObjectComparator +{ + public function accepts($expected, $actual) + { + return is_object($expected) && is_object($actual) && $actual instanceof ProphecyInterface; + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use SebastianBergmann\Comparator\Comparator; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Closure comparator. + * + * @author Konstantin Kudryashov + */ +final class ClosureComparator extends Comparator +{ + public function accepts($expected, $actual) + { + return is_object($expected) && $expected instanceof \Closure + && is_object($actual) && $actual instanceof \Closure; + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + 'all closures are born different' + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Call\Call; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Call\CallCenter; +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Exception\Prophecy\MethodProphecyException; +use Prophecy\Exception\Prediction\AggregateException; +use Prophecy\Exception\Prediction\PredictionException; + +/** + * Object prophecy. + * + * @author Konstantin Kudryashov + */ +class ObjectProphecy implements ProphecyInterface +{ + private $lazyDouble; + private $callCenter; + private $revealer; + private $comparatorFactory; + + /** + * @var MethodProphecy[][] + */ + private $methodProphecies = array(); + + /** + * Initializes object prophecy. + * + * @param LazyDouble $lazyDouble + * @param CallCenter $callCenter + * @param RevealerInterface $revealer + * @param ComparatorFactory $comparatorFactory + */ + public function __construct( + LazyDouble $lazyDouble, + CallCenter $callCenter = null, + RevealerInterface $revealer = null, + ComparatorFactory $comparatorFactory = null + ) { + $this->lazyDouble = $lazyDouble; + $this->callCenter = $callCenter ?: new CallCenter; + $this->revealer = $revealer ?: new Revealer; + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Forces double to extend specific class. + * + * @param string $class + * + * @return $this + */ + public function willExtend($class) + { + $this->lazyDouble->setParentClass($class); + + return $this; + } + + /** + * Forces double to implement specific interface. + * + * @param string $interface + * + * @return $this + */ + public function willImplement($interface) + { + $this->lazyDouble->addInterface($interface); + + return $this; + } + + /** + * Sets constructor arguments. + * + * @param array $arguments + * + * @return $this + */ + public function willBeConstructedWith(array $arguments = null) + { + $this->lazyDouble->setArguments($arguments); + + return $this; + } + + /** + * Reveals double. + * + * @return object + * + * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface + */ + public function reveal() + { + $double = $this->lazyDouble->getInstance(); + + if (null === $double || !$double instanceof ProphecySubjectInterface) { + throw new ObjectProphecyException( + "Generated double must implement ProphecySubjectInterface, but it does not.\n". + 'It seems you have wrongly configured doubler without required ClassPatch.', + $this + ); + } + + $double->setProphecy($this); + + return $double; + } + + /** + * Adds method prophecy to object prophecy. + * + * @param MethodProphecy $methodProphecy + * + * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't + * have arguments wildcard + */ + public function addMethodProphecy(MethodProphecy $methodProphecy) + { + $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); + if (null === $argumentsWildcard) { + throw new MethodProphecyException(sprintf( + "Can not add prophecy for a method `%s::%s()`\n". + "as you did not specify arguments wildcard for it.", + get_class($this->reveal()), + $methodProphecy->getMethodName() + ), $methodProphecy); + } + + $methodName = $methodProphecy->getMethodName(); + + if (!isset($this->methodProphecies[$methodName])) { + $this->methodProphecies[$methodName] = array(); + } + + $this->methodProphecies[$methodName][] = $methodProphecy; + } + + /** + * Returns either all or related to single method prophecies. + * + * @param null|string $methodName + * + * @return MethodProphecy[] + */ + public function getMethodProphecies($methodName = null) + { + if (null === $methodName) { + return $this->methodProphecies; + } + + if (!isset($this->methodProphecies[$methodName])) { + return array(); + } + + return $this->methodProphecies[$methodName]; + } + + /** + * Makes specific method call. + * + * @param string $methodName + * @param array $arguments + * + * @return mixed + */ + public function makeProphecyMethodCall($methodName, array $arguments) + { + $arguments = $this->revealer->reveal($arguments); + $return = $this->callCenter->makeCall($this, $methodName, $arguments); + + return $this->revealer->reveal($return); + } + + /** + * Finds calls by method name & arguments wildcard. + * + * @param string $methodName + * @param ArgumentsWildcard $wildcard + * + * @return Call[] + */ + public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) + { + return $this->callCenter->findCalls($methodName, $wildcard); + } + + /** + * Checks that registered method predictions do not fail. + * + * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail + */ + public function checkProphecyMethodsPredictions() + { + $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); + $exception->setObjectProphecy($this); + + foreach ($this->methodProphecies as $prophecies) { + foreach ($prophecies as $prophecy) { + try { + $prophecy->checkPrediction(); + } catch (PredictionException $e) { + $exception->append($e); + } + } + } + + if (count($exception->getExceptions())) { + throw $exception; + } + } + + /** + * Creates new method prophecy using specified method name and arguments. + * + * @param string $methodName + * @param array $arguments + * + * @return MethodProphecy + */ + public function __call($methodName, array $arguments) + { + $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments)); + + foreach ($this->getMethodProphecies($methodName) as $prophecy) { + $argumentsWildcard = $prophecy->getArgumentsWildcard(); + $comparator = $this->comparatorFactory->getComparatorFor( + $argumentsWildcard, $arguments + ); + + try { + $comparator->assertEquals($argumentsWildcard, $arguments); + return $prophecy; + } catch (ComparisonFailure $failure) {} + } + + return new MethodProphecy($this, $methodName, $arguments); + } + + /** + * Tries to get property value from double. + * + * @param string $name + * + * @return mixed + */ + public function __get($name) + { + return $this->reveal()->$name; + } + + /** + * Tries to set property value to double. + * + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->reveal()->$name = $this->revealer->reveal($value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Basic prophecies revealer. + * + * @author Konstantin Kudryashov + */ +class Revealer implements RevealerInterface +{ + /** + * Unwraps value(s). + * + * @param mixed $value + * + * @return mixed + */ + public function reveal($value) + { + if (is_array($value)) { + return array_map(array($this, __FUNCTION__), $value); + } + + if (!is_object($value)) { + return $value; + } + + if ($value instanceof ProphecyInterface) { + $value = $value->reveal(); + } + + return $value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Core Prophecy interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecyInterface +{ + /** + * Reveals prophecy object (double) . + * + * @return object + */ + public function reveal(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Prophecies revealer interface. + * + * @author Konstantin Kudryashov + */ +interface RevealerInterface +{ + /** + * Unwraps value(s). + * + * @param mixed $value + * + * @return mixed + */ + public function reveal($value); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +use Prophecy\Argument; +use Prophecy\Prophet; +use Prophecy\Promise; +use Prophecy\Prediction; +use Prophecy\Exception\Doubler\MethodNotFoundException; +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Prophecy\MethodProphecyException; + +/** + * Method prophecy. + * + * @author Konstantin Kudryashov + */ +class MethodProphecy +{ + private $objectProphecy; + private $methodName; + private $argumentsWildcard; + private $promise; + private $prediction; + private $checkedPredictions = array(); + private $bound = false; + + /** + * Initializes method prophecy. + * + * @param ObjectProphecy $objectProphecy + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + * + * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found + */ + public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null) + { + $double = $objectProphecy->reveal(); + if (!method_exists($double, $methodName)) { + throw new MethodNotFoundException(sprintf( + 'Method `%s::%s()` is not defined.', get_class($double), $methodName + ), get_class($double), $methodName, $arguments); + } + + $this->objectProphecy = $objectProphecy; + $this->methodName = $methodName; + + $reflectedMethod = new \ReflectionMethod($double, $methodName); + if ($reflectedMethod->isFinal()) { + throw new MethodProphecyException(sprintf( + "Can not add prophecy for a method `%s::%s()`\n". + "as it is a final method.", + get_class($double), + $methodName + ), $this); + } + + if (null !== $arguments) { + $this->withArguments($arguments); + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) { + $type = (string) $reflectedMethod->getReturnType(); + $this->will(function () use ($type) { + switch ($type) { + case 'string': return ''; + case 'float': return 0.0; + case 'int': return 0; + case 'bool': return false; + case 'array': return array(); + + case 'callable': + case 'Closure': + return function () {}; + + case 'Traversable': + case 'Generator': + // Remove eval() when minimum version >=5.5 + $generator = eval('return function () { yield; };'); + return $generator(); + + default: + $prophet = new Prophet; + return $prophet->prophesize($type)->reveal(); + } + }); + } + } + + /** + * Sets argument wildcard. + * + * @param array|Argument\ArgumentsWildcard $arguments + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function withArguments($arguments) + { + if (is_array($arguments)) { + $arguments = new Argument\ArgumentsWildcard($arguments); + } + + if (!$arguments instanceof Argument\ArgumentsWildcard) { + throw new InvalidArgumentException(sprintf( + "Either an array or an instance of ArgumentsWildcard expected as\n". + 'a `MethodProphecy::withArguments()` argument, but got %s.', + gettype($arguments) + )); + } + + $this->argumentsWildcard = $arguments; + + return $this; + } + + /** + * Sets custom promise to the prophecy. + * + * @param callable|Promise\PromiseInterface $promise + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function will($promise) + { + if (is_callable($promise)) { + $promise = new Promise\CallbackPromise($promise); + } + + if (!$promise instanceof Promise\PromiseInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PromiseInterface, but got %s.', + gettype($promise) + )); + } + + $this->bindToObjectProphecy(); + $this->promise = $promise; + + return $this; + } + + /** + * Sets return promise to the prophecy. + * + * @see Prophecy\Promise\ReturnPromise + * + * @return $this + */ + public function willReturn() + { + return $this->will(new Promise\ReturnPromise(func_get_args())); + } + + /** + * Sets return argument promise to the prophecy. + * + * @param int $index The zero-indexed number of the argument to return + * + * @see Prophecy\Promise\ReturnArgumentPromise + * + * @return $this + */ + public function willReturnArgument($index = 0) + { + return $this->will(new Promise\ReturnArgumentPromise($index)); + } + + /** + * Sets throw promise to the prophecy. + * + * @see Prophecy\Promise\ThrowPromise + * + * @param string|\Exception $exception Exception class or instance + * + * @return $this + */ + public function willThrow($exception) + { + return $this->will(new Promise\ThrowPromise($exception)); + } + + /** + * Sets custom prediction to the prophecy. + * + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function should($prediction) + { + if (is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PredictionInterface, but got %s.', + gettype($prediction) + )); + } + + $this->bindToObjectProphecy(); + $this->prediction = $prediction; + + return $this; + } + + /** + * Sets call prediction to the prophecy. + * + * @see Prophecy\Prediction\CallPrediction + * + * @return $this + */ + public function shouldBeCalled() + { + return $this->should(new Prediction\CallPrediction); + } + + /** + * Sets no calls prediction to the prophecy. + * + * @see Prophecy\Prediction\NoCallsPrediction + * + * @return $this + */ + public function shouldNotBeCalled() + { + return $this->should(new Prediction\NoCallsPrediction); + } + + /** + * Sets call times prediction to the prophecy. + * + * @see Prophecy\Prediction\CallTimesPrediction + * + * @param $count + * + * @return $this + */ + public function shouldBeCalledTimes($count) + { + return $this->should(new Prediction\CallTimesPrediction($count)); + } + + /** + * Checks provided prediction immediately. + * + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function shouldHave($prediction) + { + if (is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PredictionInterface, but got %s.', + gettype($prediction) + )); + } + + if (null === $this->promise) { + $this->willReturn(); + } + + $calls = $this->getObjectProphecy()->findProphecyMethodCalls( + $this->getMethodName(), + $this->getArgumentsWildcard() + ); + + try { + $prediction->check($calls, $this->getObjectProphecy(), $this); + $this->checkedPredictions[] = $prediction; + } catch (\Exception $e) { + $this->checkedPredictions[] = $prediction; + + throw $e; + } + + return $this; + } + + /** + * Checks call prediction. + * + * @see Prophecy\Prediction\CallPrediction + * + * @return $this + */ + public function shouldHaveBeenCalled() + { + return $this->shouldHave(new Prediction\CallPrediction); + } + + /** + * Checks no calls prediction. + * + * @see Prophecy\Prediction\NoCallsPrediction + * + * @return $this + */ + public function shouldNotHaveBeenCalled() + { + return $this->shouldHave(new Prediction\NoCallsPrediction); + } + + /** + * Checks no calls prediction. + * + * @see Prophecy\Prediction\NoCallsPrediction + * @deprecated + * + * @return $this + */ + public function shouldNotBeenCalled() + { + return $this->shouldNotHaveBeenCalled(); + } + + /** + * Checks call times prediction. + * + * @see Prophecy\Prediction\CallTimesPrediction + * + * @param int $count + * + * @return $this + */ + public function shouldHaveBeenCalledTimes($count) + { + return $this->shouldHave(new Prediction\CallTimesPrediction($count)); + } + + /** + * Checks currently registered [with should(...)] prediction. + */ + public function checkPrediction() + { + if (null === $this->prediction) { + return; + } + + $this->shouldHave($this->prediction); + } + + /** + * Returns currently registered promise. + * + * @return null|Promise\PromiseInterface + */ + public function getPromise() + { + return $this->promise; + } + + /** + * Returns currently registered prediction. + * + * @return null|Prediction\PredictionInterface + */ + public function getPrediction() + { + return $this->prediction; + } + + /** + * Returns predictions that were checked on this object. + * + * @return Prediction\PredictionInterface[] + */ + public function getCheckedPredictions() + { + return $this->checkedPredictions; + } + + /** + * Returns object prophecy this method prophecy is tied to. + * + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } + + /** + * Returns method name. + * + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * Returns arguments wildcard. + * + * @return Argument\ArgumentsWildcard + */ + public function getArgumentsWildcard() + { + return $this->argumentsWildcard; + } + + private function bindToObjectProphecy() + { + if ($this->bound) { + return; + } + + $this->getObjectProphecy()->addMethodProphecy($this); + $this->bound = true; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Controllable doubles interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecySubjectInterface +{ + /** + * Sets subject prophecy. + * + * @param ProphecyInterface $prophecy + */ + public function setProphecy(ProphecyInterface $prophecy); + + /** + * Returns subject prophecy. + * + * @return ProphecyInterface + */ + public function getProphecy(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return argument promise. + * + * @author Konstantin Kudryashov + */ +class ReturnArgumentPromise implements PromiseInterface +{ + /** + * @var int + */ + private $index; + + /** + * Initializes callback promise. + * + * @param int $index The zero-indexed number of the argument to return + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($index = 0) + { + if (!is_int($index) || $index < 0) { + throw new InvalidArgumentException(sprintf( + 'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', + $index + )); + } + $this->index = $index; + } + + /** + * Returns nth argument if has one, null otherwise. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return null|mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + return count($args) > $this->index ? $args[$this->index] : null; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return promise. + * + * @author Konstantin Kudryashov + */ +class ReturnPromise implements PromiseInterface +{ + private $returnValues = array(); + + /** + * Initializes promise. + * + * @param array $returnValues Array of values + */ + public function __construct(array $returnValues) + { + $this->returnValues = $returnValues; + } + + /** + * Returns saved values one by one until last one, then continuously returns last value. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + $value = array_shift($this->returnValues); + + if (!count($this->returnValues)) { + $this->returnValues[] = $value; + } + + return $value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; + +/** + * Callback promise. + * + * @author Konstantin Kudryashov + */ +class CallbackPromise implements PromiseInterface +{ + private $callback; + + /** + * Initializes callback promise. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackPromise, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Evaluates promise callback. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + $callback = $this->callback; + + if ($callback instanceof Closure && method_exists('Closure', 'bind')) { + $callback = Closure::bind($callback, $object); + } + + return call_user_func($callback, $args, $object, $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Promise interface. + * Promises are logical blocks, tied to `will...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PromiseInterface +{ + /** + * Evaluates promise. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Doctrine\Instantiator\Instantiator; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; + +/** + * Throw promise. + * + * @author Konstantin Kudryashov + */ +class ThrowPromise implements PromiseInterface +{ + private $exception; + + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * Initializes promise. + * + * @param string|\Exception $exception Exception class name or instance + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($exception) + { + if (is_string($exception)) { + if (!class_exists($exception) + && 'Exception' !== $exception + && !is_subclass_of($exception, 'Exception')) { + throw new InvalidArgumentException(sprintf( + 'Exception class or instance expected as argument to ThrowPromise, but got %s.', + $exception + )); + } + } elseif (!$exception instanceof \Exception) { + throw new InvalidArgumentException(sprintf( + 'Exception class or instance expected as argument to ThrowPromise, but got %s.', + is_object($exception) ? get_class($exception) : gettype($exception) + )); + } + + $this->exception = $exception; + } + + /** + * Throws predefined exception. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws object + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + if (is_string($this->exception)) { + $classname = $this->exception; + $reflection = new ReflectionClass($classname); + $constructor = $reflection->getConstructor(); + + if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { + throw $reflection->newInstance(); + } + + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); + } + + throw $this->instantiator->instantiate($classname); + } + + throw $this->exception; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument; + +/** + * Arguments wildcarding. + * + * @author Konstantin Kudryashov + */ +class ArgumentsWildcard +{ + /** + * @var Token\TokenInterface[] + */ + private $tokens = array(); + private $string; + + /** + * Initializes wildcard. + * + * @param array $arguments Array of argument tokens or values + */ + public function __construct(array $arguments) + { + foreach ($arguments as $argument) { + if (!$argument instanceof Token\TokenInterface) { + $argument = new Token\ExactValueToken($argument); + } + + $this->tokens[] = $argument; + } + } + + /** + * Calculates wildcard match score for provided arguments. + * + * @param array $arguments + * + * @return false|int False OR integer score (higher - better) + */ + public function scoreArguments(array $arguments) + { + if (0 == count($arguments) && 0 == count($this->tokens)) { + return 1; + } + + $arguments = array_values($arguments); + $totalScore = 0; + foreach ($this->tokens as $i => $token) { + $argument = isset($arguments[$i]) ? $arguments[$i] : null; + if (1 >= $score = $token->scoreArgument($argument)) { + return false; + } + + $totalScore += $score; + + if (true === $token->isLast()) { + return $totalScore; + } + } + + if (count($arguments) > count($this->tokens)) { + return false; + } + + return $totalScore; + } + + /** + * Returns string representation for wildcard. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = implode(', ', array_map(function ($token) { + return (string) $token; + }, $this->tokens)); + } + + return $this->string; + } + + /** + * @return array + */ + public function getTokens() + { + return $this->tokens; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Approximate value token + * + * @author Daniel Leech + */ +class ApproximateValueToken implements TokenInterface +{ + private $value; + private $precision; + + public function __construct($value, $precision = 0) + { + $this->value = $value; + $this->precision = $precision; + } + + /** + * {@inheritdoc} + */ + public function scoreArgument($argument) + { + return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false; + } + + /** + * {@inheritdoc} + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('≅%s', round($this->value, $this->precision)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * String contains token. + * + * @author Peter Mitchell + */ +class StringContainsToken implements TokenInterface +{ + private $value; + + /** + * Initializes token. + * + * @param string $value + */ + public function __construct($value) + { + $this->value = $value; + } + + public function scoreArgument($argument) + { + return strpos($argument, $this->value) !== false ? 6 : false; + } + + /** + * Returns preset value against which token checks arguments. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('contains("%s")', $this->value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Argument token interface. + * + * @author Konstantin Kudryashov + */ +interface TokenInterface +{ + /** + * Calculates token match score for provided argument. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument); + + /** + * Returns true if this token prevents check of other tokens (is last one). + * + * @return bool|int + */ + public function isLast(); + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Any single value token. + * + * @author Konstantin Kudryashov + */ +class AnyValueToken implements TokenInterface +{ + /** + * Always scores 3 for any argument. + * + * @param $argument + * + * @return int + */ + public function scoreArgument($argument) + { + return 3; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return '*'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Array elements count token. + * + * @author Boris Mikhaylov + */ + +class ArrayCountToken implements TokenInterface +{ + private $count; + + /** + * @param integer $value + */ + public function __construct($value) + { + $this->count = $value; + } + + /** + * Scores 6 when argument has preset number of elements. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('count(%s)', $this->count); + } + + /** + * Returns true if object is either array or instance of \Countable + * + * @param $argument + * @return bool + */ + private function isCountable($argument) + { + return (is_array($argument) || $argument instanceof \Countable); + } + + /** + * Returns true if $argument has expected number of elements + * + * @param array|\Countable $argument + * + * @return bool + */ + private function hasProperCount($argument) + { + return $this->count === count($argument); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Util\StringUtil; + +/** + * Identical value token. + * + * @author Florian Voutzinos + */ +class IdenticalValueToken implements TokenInterface +{ + private $value; + private $string; + private $util; + + /** + * Initializes token. + * + * @param mixed $value + * @param StringUtil $util + */ + public function __construct($value, StringUtil $util = null) + { + $this->value = $value; + $this->util = $util ?: new StringUtil(); + } + + /** + * Scores 11 if argument matches preset value. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return $argument === $this->value ? 11 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = sprintf('identical(%s)', $this->util->stringify($this->value)); + } + + return $this->string; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Callback-verified token. + * + * @author Konstantin Kudryashov + */ +class CallbackToken implements TokenInterface +{ + private $callback; + + /** + * Initializes token. + * + * @param callable $callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackToken, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Scores 7 if callback returns true, false otherwise. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return call_user_func($this->callback, $argument) ? 7 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return 'callback()'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Array every entry token. + * + * @author Adrien Brault + */ +class ArrayEveryEntryToken implements TokenInterface +{ + /** + * @var TokenInterface + */ + private $value; + + /** + * @param mixed $value exact value or token + */ + public function __construct($value) + { + if (!$value instanceof TokenInterface) { + $value = new ExactValueToken($value); + } + + $this->value = $value; + } + + /** + * {@inheritdoc} + */ + public function scoreArgument($argument) + { + if (!$argument instanceof \Traversable && !is_array($argument)) { + return false; + } + + $scores = array(); + foreach ($argument as $key => $argumentEntry) { + $scores[] = $this->value->scoreArgument($argumentEntry); + } + + if (empty($scores) || in_array(false, $scores, true)) { + return false; + } + + return array_sum($scores) / count($scores); + } + + /** + * {@inheritdoc} + */ + public function isLast() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('[%s, ..., %s]', $this->value, $this->value); + } + + /** + * @return TokenInterface + */ + public function getValue() + { + return $this->value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Array entry token. + * + * @author Boris Mikhaylov + */ +class ArrayEntryToken implements TokenInterface +{ + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $key; + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $value; + + /** + * @param mixed $key exact value or token + * @param mixed $value exact value or token + */ + public function __construct($key, $value) + { + $this->key = $this->wrapIntoExactValueToken($key); + $this->value = $this->wrapIntoExactValueToken($value); + } + + /** + * Scores half of combined scores from key and value tokens for same entry. Capped at 8. + * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. + * + * @param array|\ArrayAccess|\Traversable $argument + * + * @throws \Prophecy\Exception\InvalidArgumentException + * @return bool|int + */ + public function scoreArgument($argument) + { + if ($argument instanceof \Traversable) { + $argument = iterator_to_array($argument); + } + + if ($argument instanceof \ArrayAccess) { + $argument = $this->convertArrayAccessToEntry($argument); + } + + if (!is_array($argument) || empty($argument)) { + return false; + } + + $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument)); + $valueScores = array_map(array($this->value,'scoreArgument'), $argument); + $scoreEntry = function ($value, $key) { + return $value && $key ? min(8, ($key + $value) / 2) : false; + }; + + return max(array_map($scoreEntry, $valueScores, $keyScores)); + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('[..., %s => %s, ...]', $this->key, $this->value); + } + + /** + * Returns key + * + * @return TokenInterface + */ + public function getKey() + { + return $this->key; + } + + /** + * Returns value + * + * @return TokenInterface + */ + public function getValue() + { + return $this->value; + } + + /** + * Wraps non token $value into ExactValueToken + * + * @param $value + * @return TokenInterface + */ + private function wrapIntoExactValueToken($value) + { + return $value instanceof TokenInterface ? $value : new ExactValueToken($value); + } + + /** + * Converts instance of \ArrayAccess to key => value array entry + * + * @param \ArrayAccess $object + * + * @return array|null + * @throws \Prophecy\Exception\InvalidArgumentException + */ + private function convertArrayAccessToEntry(\ArrayAccess $object) + { + if (!$this->key instanceof ExactValueToken) { + throw new InvalidArgumentException(sprintf( + 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL. + 'But you used `%s`.', + $this->key + )); + } + + $key = $this->key->getValue(); + + return $object->offsetExists($key) ? array($key => $object[$key]) : array(); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Logical NOT token. + * + * @author Boris Mikhaylov + */ +class LogicalNotToken implements TokenInterface +{ + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $token; + + /** + * @param mixed $value exact value or token + */ + public function __construct($value) + { + $this->token = $value instanceof TokenInterface? $value : new ExactValueToken($value); + } + + /** + * Scores 4 when preset token does not match the argument. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return false === $this->token->scoreArgument($argument) ? 4 : false; + } + + /** + * Returns true if preset token is last. + * + * @return bool|int + */ + public function isLast() + { + return $this->token->isLast(); + } + + /** + * Returns originating token. + * + * @return TokenInterface + */ + public function getOriginatingToken() + { + return $this->token; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('not(%s)', $this->token); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Value type token. + * + * @author Konstantin Kudryashov + */ +class TypeToken implements TokenInterface +{ + private $type; + + /** + * @param string $type + */ + public function __construct($type) + { + $checker = "is_{$type}"; + if (!function_exists($checker) && !interface_exists($type) && !class_exists($type)) { + throw new InvalidArgumentException(sprintf( + 'Type or class name expected as an argument to TypeToken, but got %s.', $type + )); + } + + $this->type = $type; + } + + /** + * Scores 5 if argument has the same type this token was constructed with. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + $checker = "is_{$this->type}"; + if (function_exists($checker)) { + return call_user_func($checker, $argument) ? 5 : false; + } + + return $argument instanceof $this->type ? 5 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('type(%s)', $this->type); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Exact value token. + * + * @author Konstantin Kudryashov + */ +class ExactValueToken implements TokenInterface +{ + private $value; + private $string; + private $util; + private $comparatorFactory; + + /** + * Initializes token. + * + * @param mixed $value + * @param StringUtil $util + * @param ComparatorFactory $comparatorFactory + */ + public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) + { + $this->value = $value; + $this->util = $util ?: new StringUtil(); + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Scores 10 if argument matches preset value. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (is_object($argument) && is_object($this->value)) { + $comparator = $this->comparatorFactory->getComparatorFor( + $argument, $this->value + ); + + try { + $comparator->assertEquals($argument, $this->value); + return 10; + } catch (ComparisonFailure $failure) {} + } + + // If either one is an object it should be castable to a string + if (is_object($argument) xor is_object($this->value)) { + if (is_object($argument) && !method_exists($argument, '__toString')) { + return false; + } + + if (is_object($this->value) && !method_exists($this->value, '__toString')) { + return false; + } + } elseif (is_numeric($argument) && is_numeric($this->value)) { + // noop + } elseif (gettype($argument) !== gettype($this->value)) { + return false; + } + + return $argument == $this->value ? 10 : false; + } + + /** + * Returns preset value against which token checks arguments. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = sprintf('exact(%s)', $this->util->stringify($this->value)); + } + + return $this->string; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Any values token. + * + * @author Konstantin Kudryashov + */ +class AnyValuesToken implements TokenInterface +{ + /** + * Always scores 2 for any argument. + * + * @param $argument + * + * @return int + */ + public function scoreArgument($argument) + { + return 2; + } + + /** + * Returns true to stop wildcard from processing other tokens. + * + * @return bool + */ + public function isLast() + { + return true; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return '* [, ...]'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Logical AND token. + * + * @author Boris Mikhaylov + */ +class LogicalAndToken implements TokenInterface +{ + private $tokens = array(); + + /** + * @param array $arguments exact values or tokens + */ + public function __construct(array $arguments) + { + foreach ($arguments as $argument) { + if (!$argument instanceof TokenInterface) { + $argument = new ExactValueToken($argument); + } + $this->tokens[] = $argument; + } + } + + /** + * Scores maximum score from scores returned by tokens for this argument if all of them score. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (0 === count($this->tokens)) { + return false; + } + + $maxScore = 0; + foreach ($this->tokens as $token) { + $score = $token->scoreArgument($argument); + if (false === $score) { + return false; + } + $maxScore = max($score, $maxScore); + } + + return $maxScore; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('bool(%s)', implode(' AND ', $this->tokens)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Object state-checker token. + * + * @author Konstantin Kudryashov + */ +class ObjectStateToken implements TokenInterface +{ + private $name; + private $value; + private $util; + private $comparatorFactory; + + /** + * Initializes token. + * + * @param string $methodName + * @param mixed $value Expected return value + * @param null|StringUtil $util + * @param ComparatorFactory $comparatorFactory + */ + public function __construct( + $methodName, + $value, + StringUtil $util = null, + ComparatorFactory $comparatorFactory = null + ) { + $this->name = $methodName; + $this->value = $value; + $this->util = $util ?: new StringUtil; + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Scores 8 if argument is an object, which method returns expected value. + * + * @param mixed $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (is_object($argument) && method_exists($argument, $this->name)) { + $actual = call_user_func(array($argument, $this->name)); + + $comparator = $this->comparatorFactory->getComparatorFor( + $actual, $this->value + ); + + try { + $comparator->assertEquals($actual, $this->value); + return 8; + } catch (ComparisonFailure $failure) { + return false; + } + } + + if (is_object($argument) && property_exists($argument, $this->name)) { + return $argument->{$this->name} === $this->value ? 8 : false; + } + + return false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('state(%s(), %s)', + $this->name, + $this->util->stringify($this->value) + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Call; + +use Exception; + +/** + * Call object. + * + * @author Konstantin Kudryashov + */ +class Call +{ + private $methodName; + private $arguments; + private $returnValue; + private $exception; + private $file; + private $line; + + /** + * Initializes call. + * + * @param string $methodName + * @param array $arguments + * @param mixed $returnValue + * @param Exception $exception + * @param null|string $file + * @param null|int $line + */ + public function __construct($methodName, array $arguments, $returnValue, + Exception $exception = null, $file, $line) + { + $this->methodName = $methodName; + $this->arguments = $arguments; + $this->returnValue = $returnValue; + $this->exception = $exception; + + if ($file) { + $this->file = $file; + $this->line = intval($line); + } + } + + /** + * Returns called method name. + * + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * Returns called method arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns called method return value. + * + * @return null|mixed + */ + public function getReturnValue() + { + return $this->returnValue; + } + + /** + * Returns exception that call thrown. + * + * @return null|Exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Returns callee filename. + * + * @return string + */ + public function getFile() + { + return $this->file; + } + + /** + * Returns callee line number. + * + * @return int + */ + public function getLine() + { + return $this->line; + } + + /** + * Returns short notation for callee place. + * + * @return string + */ + public function getCallPlace() + { + if (null === $this->file) { + return 'unknown'; + } + + return sprintf('%s:%d', $this->file, $this->line); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Call; + +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Call\UnexpectedCallException; + +/** + * Calls receiver & manager. + * + * @author Konstantin Kudryashov + */ +class CallCenter +{ + private $util; + + /** + * @var Call[] + */ + private $recordedCalls = array(); + + /** + * Initializes call center. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Makes and records specific method call for object prophecy. + * + * @param ObjectProphecy $prophecy + * @param string $methodName + * @param array $arguments + * + * @return mixed Returns null if no promise for prophecy found or promise return value. + * + * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found + */ + public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) + { + // For efficiency exclude 'args' from the generated backtrace + if (PHP_VERSION_ID >= 50400) { + // Limit backtrace to last 3 calls as we don't use the rest + // Limit argument was introduced in PHP 5.4.0 + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); + } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { + // DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6 + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } else { + $backtrace = debug_backtrace(); + } + + $file = $line = null; + if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { + $file = $backtrace[2]['file']; + $line = $backtrace[2]['line']; + } + + // If no method prophecies defined, then it's a dummy, so we'll just return null + if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) { + $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); + + return null; + } + + // There are method prophecies, so it's a fake/stub. Searching prophecy for this call + $matches = array(); + foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { + if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { + $matches[] = array($score, $methodProphecy); + } + } + + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!count($matches)) { + throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments); + } + + // Sort matches by their score value + @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; }); + + // If Highest rated method prophecy has a promise - execute it or return null instead + $returnValue = null; + $exception = null; + if ($promise = $matches[0][1]->getPromise()) { + try { + $returnValue = $promise->execute($arguments, $prophecy, $matches[0][1]); + } catch (\Exception $e) { + $exception = $e; + } + } + + $this->recordedCalls[] = new Call( + $methodName, $arguments, $returnValue, $exception, $file, $line + ); + + if (null !== $exception) { + throw $exception; + } + + return $returnValue; + } + + /** + * Searches for calls by method name & arguments wildcard. + * + * @param string $methodName + * @param ArgumentsWildcard $wildcard + * + * @return Call[] + */ + public function findCalls($methodName, ArgumentsWildcard $wildcard) + { + return array_values( + array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) { + return $methodName === $call->getMethodName() + && 0 < $wildcard->scoreArguments($call->getArguments()) + ; + }) + ); + } + + private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, + array $arguments) + { + $classname = get_class($prophecy->reveal()); + $argstring = implode(', ', array_map(array($this->util, 'stringify'), $arguments)); + $expected = implode("\n", array_map(function (MethodProphecy $methodProphecy) { + return sprintf(' - %s(%s)', + $methodProphecy->getMethodName(), + $methodProphecy->getArgumentsWildcard() + ); + }, call_user_func_array('array_merge', $prophecy->getMethodProphecies()))); + + return new UnexpectedCallException( + sprintf( + "Method call:\n". + " - %s(%s)\n". + "on %s was not expected, expected calls were:\n%s", + + $methodName, $argstring, $classname, $expected + ), + $prophecy, $methodName, $arguments + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\NoCallsException; + +/** + * Call prediction. + * + * @author Konstantin Kudryashov + */ +class CallPrediction implements PredictionInterface +{ + private $util; + + /** + * Initializes prediction. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there was at least one call. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\NoCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if (count($calls)) { + return; + } + + $methodCalls = $object->findProphecyMethodCalls( + $method->getMethodName(), + new ArgumentsWildcard(array(new AnyValuesToken)) + ); + + if (count($methodCalls)) { + throw new NoCallsException(sprintf( + "No calls have been made that match:\n". + " %s->%s(%s)\n". + "but expected at least one.\n". + "Recorded `%s(...)` calls:\n%s", + + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + $method->getMethodName(), + $this->util->stringifyCalls($methodCalls) + ), $method); + } + + throw new NoCallsException(sprintf( + "No calls have been made that match:\n". + " %s->%s(%s)\n". + "but expected at least one.", + + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard() + ), $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; + +/** + * Callback prediction. + * + * @author Konstantin Kudryashov + */ +class CallbackPrediction implements PredictionInterface +{ + private $callback; + + /** + * Initializes callback prediction. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackPrediction, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Executes preset callback. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + $callback = $this->callback; + + if ($callback instanceof Closure && method_exists('Closure', 'bind')) { + $callback = Closure::bind($callback, $object); + } + + call_user_func($callback, $calls, $object, $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsException; + +/** + * No calls prediction. + * + * @author Konstantin Kudryashov + */ +class NoCallsPrediction implements PredictionInterface +{ + private $util; + + /** + * Initializes prediction. + * + * @param null|StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there were no calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if (!count($calls)) { + return; + } + + $verb = count($calls) === 1 ? 'was' : 'were'; + + throw new UnexpectedCallsException(sprintf( + "No calls expected that match:\n". + " %s->%s(%s)\n". + "but %d %s made:\n%s", + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + count($calls), + $verb, + $this->util->stringifyCalls($calls) + ), $method, $calls); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsCountException; + +/** + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov + */ +class CallTimesPrediction implements PredictionInterface +{ + private $times; + private $util; + + /** + * Initializes prediction. + * + * @param int $times + * @param StringUtil $util + */ + public function __construct($times, StringUtil $util = null) + { + $this->times = intval($times); + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there was exact amount of calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if ($this->times == count($calls)) { + return; + } + + $methodCalls = $object->findProphecyMethodCalls( + $method->getMethodName(), + new ArgumentsWildcard(array(new AnyValuesToken)) + ); + + if (count($calls)) { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but %d were made:\n%s", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + count($calls), + $this->util->stringifyCalls($calls) + ); + } elseif (count($methodCalls)) { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but none were made.\n". + "Recorded `%s(...)` calls:\n%s", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + $method->getMethodName(), + $this->util->stringifyCalls($methodCalls) + ); + } else { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but none were made.", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard() + ); + } + + throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PredictionInterface +{ + /** + * Tests that double fulfilled prediction. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws object + * @return void + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Util; + +use Prophecy\Call\Call; + +/** + * String utility. + * + * @author Konstantin Kudryashov + */ +class StringUtil +{ + /** + * Stringifies any provided value. + * + * @param mixed $value + * @param boolean $exportObject + * + * @return string + */ + public function stringify($value, $exportObject = true) + { + if (is_array($value)) { + if (range(0, count($value) - 1) === array_keys($value)) { + return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']'; + } + + $stringify = array($this, __FUNCTION__); + + return '['.implode(', ', array_map(function ($item, $key) use ($stringify) { + return (is_integer($key) ? $key : '"'.$key.'"'). + ' => '.call_user_func($stringify, $item); + }, $value, array_keys($value))).']'; + } + if (is_resource($value)) { + return get_resource_type($value).':'.$value; + } + if (is_object($value)) { + return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value)); + } + if (true === $value || false === $value) { + return $value ? 'true' : 'false'; + } + if (is_string($value)) { + $str = sprintf('"%s"', str_replace("\n", '\\n', $value)); + + if (50 <= strlen($str)) { + return substr($str, 0, 50).'"...'; + } + + return $str; + } + if (null === $value) { + return 'null'; + } + + return (string) $value; + } + + /** + * Stringifies provided array of calls. + * + * @param Call[] $calls Array of Call instances + * + * @return string + */ + public function stringifyCalls(array $calls) + { + $self = $this; + + return implode(PHP_EOL, array_map(function (Call $call) use ($self) { + return sprintf(' - %s(%s) @ %s', + $call->getMethodName(), + implode(', ', array_map(array($self, 'stringify'), $call->getArguments())), + str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace()) + ); + }, $calls)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This class is a modification from sebastianbergmann/exporter + * @see https://github.com/sebastianbergmann/exporter + */ +class ExportUtil +{ + /** + * Exports a value as a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value + * @param int $indentation The indentation level of the 2nd+ line + * @return string + */ + public static function export($value, $indentation = 0) + { + return self::recursiveExport($value, $indentation); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param mixed $value + * @return array + */ + public static function toArray($value) + { + if (!is_object($value)) { + return (array) $value; + } + + $array = array(); + + foreach ((array) $value as $key => $val) { + // properties are transformed to keys in the following way: + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\0gcdata") { + continue; + } + + $array[$key] = $val; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection in Zend. + // Format the output similarly to print_r() in this case + if ($value instanceof \SplObjectStorage) { + // However, the fast method does work in HHVM, and exposes the + // internal implementation. Hide it again. + if (property_exists('\SplObjectStorage', '__storage')) { + unset($array['__storage']); + } elseif (property_exists('\SplObjectStorage', 'storage')) { + unset($array['storage']); + } + + if (property_exists('\SplObjectStorage', '__key')) { + unset($array['__key']); + } + + foreach ($value as $key => $val) { + $array[spl_object_hash($val)] = array( + 'obj' => $val, + 'inf' => $value->getInfo(), + ); + } + } + + return $array; + } + + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param int $indentation The indentation level of the 2nd+ line + * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * @return string + * @see SebastianBergmann\Exporter\Exporter::export + */ + protected static function recursiveExport(&$value, $indentation, $processed = null) + { + if ($value === null) { + return 'null'; + } + + if ($value === true) { + return 'true'; + } + + if ($value === false) { + return 'false'; + } + + if (is_float($value) && floatval(intval($value)) === $value) { + return "$value.0"; + } + + if (is_resource($value)) { + return sprintf( + 'resource(%d) of type (%s)', + $value, + get_resource_type($value) + ); + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . + str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . + "'"; + } + + $whitespace = str_repeat(' ', 4 * $indentation); + + if (!$processed) { + $processed = new Context; + } + + if (is_array($value)) { + if (($key = $processed->contains($value)) !== false) { + return 'Array &' . $key; + } + + $key = $processed->add($value); + $values = ''; + + if (count($value) > 0) { + foreach ($value as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + self::recursiveExport($k, $indentation), + self::recursiveExport($value[$k], $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('Array &%s (%s)', $key, $values); + } + + if (is_object($value)) { + $class = get_class($value); + + if ($value instanceof ProphecyInterface) { + return sprintf('%s Object (*Prophecy*)', $class); + } elseif ($hash = $processed->contains($value)) { + return sprintf('%s:%s Object', $class, $hash); + } + + $hash = $processed->add($value); + $values = ''; + $array = self::toArray($value); + + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + self::recursiveExport($k, $indentation), + self::recursiveExport($v, $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('%s:%s Object (%s)', $class, $hash, $values); + } + + return var_export($value, true); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy; + +use Prophecy\Doubler\Doubler; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Doubler\ClassPatch; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\RevealerInterface; +use Prophecy\Prophecy\Revealer; +use Prophecy\Call\CallCenter; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Exception\Prediction\AggregateException; + +/** + * Prophet creates prophecies. + * + * @author Konstantin Kudryashov + */ +class Prophet +{ + private $doubler; + private $revealer; + private $util; + + /** + * @var ObjectProphecy[] + */ + private $prophecies = array(); + + /** + * Initializes Prophet. + * + * @param null|Doubler $doubler + * @param null|RevealerInterface $revealer + * @param null|StringUtil $util + */ + public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null, + StringUtil $util = null) + { + if (null === $doubler) { + $doubler = new Doubler; + $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch); + $doubler->registerClassPatch(new ClassPatch\TraversablePatch); + $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch); + $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch); + $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch); + $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch()); + $doubler->registerClassPatch(new ClassPatch\MagicCallPatch); + $doubler->registerClassPatch(new ClassPatch\KeywordPatch); + } + + $this->doubler = $doubler; + $this->revealer = $revealer ?: new Revealer; + $this->util = $util ?: new StringUtil; + } + + /** + * Creates new object prophecy. + * + * @param null|string $classOrInterface Class or interface name + * + * @return ObjectProphecy + */ + public function prophesize($classOrInterface = null) + { + $this->prophecies[] = $prophecy = new ObjectProphecy( + new LazyDouble($this->doubler), + new CallCenter($this->util), + $this->revealer + ); + + if ($classOrInterface && class_exists($classOrInterface)) { + return $prophecy->willExtend($classOrInterface); + } + + if ($classOrInterface && interface_exists($classOrInterface)) { + return $prophecy->willImplement($classOrInterface); + } + + return $prophecy; + } + + /** + * Returns all created object prophecies. + * + * @return ObjectProphecy[] + */ + public function getProphecies() + { + return $this->prophecies; + } + + /** + * Returns Doubler instance assigned to this Prophet. + * + * @return Doubler + */ + public function getDoubler() + { + return $this->doubler; + } + + /** + * Checks all predictions defined by prophecies of this Prophet. + * + * @throws Exception\Prediction\AggregateException If any prediction fails + */ + public function checkPredictions() + { + $exception = new AggregateException("Some predictions failed:\n"); + foreach ($this->prophecies as $prophecy) { + try { + $prophecy->checkProphecyMethodsPredictions(); + } catch (PredictionException $e) { + $exception->append($e); + } + } + + if (count($exception->getExceptions())) { + throw $exception; + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Exception\Exception; + +interface ProphecyException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Prophecy\MethodProphecy; + +class MethodProphecyException extends ObjectProphecyException +{ + private $methodProphecy; + + public function __construct($message, MethodProphecy $methodProphecy) + { + parent::__construct($message, $methodProphecy->getObjectProphecy()); + + $this->methodProphecy = $methodProphecy; + } + + /** + * @return MethodProphecy + */ + public function getMethodProphecy() + { + return $this->methodProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Prophecy\ObjectProphecy; + +class ObjectProphecyException extends \RuntimeException implements ProphecyException +{ + private $objectProphecy; + + public function __construct($message, ObjectProphecy $objectProphecy) + { + parent::__construct($message); + + $this->objectProphecy = $objectProphecy; + } + + /** + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Call; + +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Prophecy\ObjectProphecy; + +class UnexpectedCallException extends ObjectProphecyException +{ + private $methodName; + private $arguments; + + public function __construct($message, ObjectProphecy $objectProphecy, + $methodName, array $arguments) + { + parent::__construct($message, $objectProphecy); + + $this->methodName = $methodName; + $this->arguments = $arguments; + } + + public function getMethodName() + { + return $this->methodName; + } + + public function getArguments() + { + return $this->arguments; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\ObjectProphecy; + +class AggregateException extends \RuntimeException implements PredictionException +{ + private $exceptions = array(); + private $objectProphecy; + + public function append(PredictionException $exception) + { + $message = $exception->getMessage(); + $message = ' '.strtr($message, array("\n" => "\n "))."\n"; + + $this->message = rtrim($this->message.$message); + $this->exceptions[] = $exception; + } + + /** + * @return PredictionException[] + */ + public function getExceptions() + { + return $this->exceptions; + } + + public function setObjectProphecy(ObjectProphecy $objectProphecy) + { + $this->objectProphecy = $objectProphecy; + } + + /** + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\Prophecy\MethodProphecyException; + +class UnexpectedCallsException extends MethodProphecyException implements PredictionException +{ + private $calls = array(); + + public function __construct($message, MethodProphecy $methodProphecy, array $calls) + { + parent::__construct($message, $methodProphecy); + + $this->calls = $calls; + } + + public function getCalls() + { + return $this->calls; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Exception; + +interface PredictionException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; + +class UnexpectedCallsCountException extends UnexpectedCallsException +{ + private $expectedCount; + + public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls) + { + parent::__construct($message, $methodProphecy, $calls); + + $this->expectedCount = intval($count); + } + + public function getExpectedCount() + { + return $this->expectedCount; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use RuntimeException; + +/** + * Basic failed prediction exception. + * Use it for custom prediction failures. + * + * @author Konstantin Kudryashov + */ +class FailedPredictionException extends RuntimeException implements PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Prophecy\MethodProphecyException; + +class NoCallsException extends MethodProphecyException implements PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class MethodNotFoundException extends DoubleException +{ + /** + * @var string + */ + private $classname; + + /** + * @var string + */ + private $methodName; + + /** + * @var array + */ + private $arguments; + + /** + * @param string $message + * @param string $classname + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + */ + public function __construct($message, $classname, $methodName, $arguments = null) + { + parent::__construct($message); + + $this->classname = $classname; + $this->methodName = $methodName; + $this->arguments = $arguments; + } + + public function getClassname() + { + return $this->classname; + } + + public function getMethodName() + { + return $this->methodName; + } + + public function getArguments() + { + return $this->arguments; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use ReflectionClass; + +class ClassMirrorException extends \RuntimeException implements DoublerException +{ + private $class; + + public function __construct($message, ReflectionClass $class) + { + parent::__construct($message); + + $this->class = $class; + } + + public function getReflectedClass() + { + return $this->class; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use RuntimeException; + +class DoubleException extends RuntimeException implements DoublerException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use Prophecy\Exception\Exception; + +interface DoublerException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class ReturnByReferenceException extends DoubleException +{ + private $classname; + private $methodName; + + /** + * @param string $message + * @param string $classname + * @param string $methodName + */ + public function __construct($message, $classname, $methodName) + { + parent::__construct($message); + + $this->classname = $classname; + $this->methodName = $methodName; + } + + public function getClassname() + { + return $this->classname; + } + + public function getMethodName() + { + return $this->methodName; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class ClassNotFoundException extends DoubleException +{ + private $classname; + + /** + * @param string $message + * @param string $classname + */ + public function __construct($message, $classname) + { + parent::__construct($message); + + $this->classname = $classname; + } + + public function getClassname() + { + return $this->classname; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class InterfaceNotFoundException extends ClassNotFoundException +{ + public function getInterfaceName() + { + return $this->getClassname(); + } +} +methodName = $methodName; + $this->className = $className; + } + + + /** + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->className; + } + + } + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +class ClassCreatorException extends \RuntimeException implements DoublerException +{ + private $node; + + public function __construct($message, ClassNode $node) + { + parent::__construct($message); + + $this->node = $node; + } + + public function getClassNode() + { + return $this->node; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception; + +/** + * Core Prophecy exception interface. + * All Prophecy exceptions implement it. + * + * @author Konstantin Kudryashov + */ +interface Exception +{ + /** + * @return string + */ + public function getMessage(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy; + +use Prophecy\Argument\Token; + +/** + * Argument tokens shortcuts. + * + * @author Konstantin Kudryashov + */ +class Argument +{ + /** + * Checks that argument is exact value or object. + * + * @param mixed $value + * + * @return Token\ExactValueToken + */ + public static function exact($value) + { + return new Token\ExactValueToken($value); + } + + /** + * Checks that argument is of specific type or instance of specific class. + * + * @param string $type Type name (`integer`, `string`) or full class name + * + * @return Token\TypeToken + */ + public static function type($type) + { + return new Token\TypeToken($type); + } + + /** + * Checks that argument object has specific state. + * + * @param string $methodName + * @param mixed $value + * + * @return Token\ObjectStateToken + */ + public static function which($methodName, $value) + { + return new Token\ObjectStateToken($methodName, $value); + } + + /** + * Checks that argument matches provided callback. + * + * @param callable $callback + * + * @return Token\CallbackToken + */ + public static function that($callback) + { + return new Token\CallbackToken($callback); + } + + /** + * Matches any single value. + * + * @return Token\AnyValueToken + */ + public static function any() + { + return new Token\AnyValueToken; + } + + /** + * Matches all values to the rest of the signature. + * + * @return Token\AnyValuesToken + */ + public static function cetera() + { + return new Token\AnyValuesToken; + } + + /** + * Checks that argument matches all tokens + * + * @param mixed ... a list of tokens + * + * @return Token\LogicalAndToken + */ + public static function allOf() + { + return new Token\LogicalAndToken(func_get_args()); + } + + /** + * Checks that argument array or countable object has exact number of elements. + * + * @param integer $value array elements count + * + * @return Token\ArrayCountToken + */ + public static function size($value) + { + return new Token\ArrayCountToken($value); + } + + /** + * Checks that argument array contains (key, value) pair + * + * @param mixed $key exact value or token + * @param mixed $value exact value or token + * + * @return Token\ArrayEntryToken + */ + public static function withEntry($key, $value) + { + return new Token\ArrayEntryToken($key, $value); + } + + /** + * Checks that arguments array entries all match value + * + * @param mixed $value + * + * @return Token\ArrayEveryEntryToken + */ + public static function withEveryEntry($value) + { + return new Token\ArrayEveryEntryToken($value); + } + + /** + * Checks that argument array contains value + * + * @param mixed $value + * + * @return Token\ArrayEntryToken + */ + public static function containing($value) + { + return new Token\ArrayEntryToken(self::any(), $value); + } + + /** + * Checks that argument array has key + * + * @param mixed $key exact value or token + * + * @return Token\ArrayEntryToken + */ + public static function withKey($key) + { + return new Token\ArrayEntryToken($key, self::any()); + } + + /** + * Checks that argument does not match the value|token. + * + * @param mixed $value either exact value or argument token + * + * @return Token\LogicalNotToken + */ + public static function not($value) + { + return new Token\LogicalNotToken($value); + } + + /** + * @param string $value + * + * @return Token\StringContainsToken + */ + public static function containingString($value) + { + return new Token\StringContainsToken($value); + } + + /** + * Checks that argument is identical value. + * + * @param mixed $value + * + * @return Token\IdenticalValueToken + */ + public static function is($value) + { + return new Token\IdenticalValueToken($value); + } + + /** + * Check that argument is same value when rounding to the + * given precision. + * + * @param float $value + * @param float $precision + * + * @return Token\ApproximateValueToken + */ + public static function approximate($value, $precision = 0) + { + return new Token\ApproximateValueToken($value, $precision); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +/** + * Class code creator. + * Generates PHP code for specific class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassCodeGenerator +{ + /** + * Generates PHP code for class node. + * + * @param string $classname + * @param Node\ClassNode $class + * + * @return string + */ + public function generate($classname, Node\ClassNode $class) + { + $parts = explode('\\', $classname); + $classname = array_pop($parts); + $namespace = implode('\\', $parts); + + $code = sprintf("class %s extends \%s implements %s {\n", + $classname, $class->getParentClass(), implode(', ', + array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces()) + ) + ); + + foreach ($class->getProperties() as $name => $visibility) { + $code .= sprintf("%s \$%s;\n", $visibility, $name); + } + $code .= "\n"; + + foreach ($class->getMethods() as $method) { + $code .= $this->generateMethod($method)."\n"; + } + $code .= "\n}"; + + return sprintf("namespace %s {\n%s\n}", $namespace, $code); + } + + private function generateMethod(Node\MethodNode $method) + { + $php = sprintf("%s %s function %s%s(%s)%s {\n", + $method->getVisibility(), + $method->isStatic() ? 'static' : '', + $method->returnsReference() ? '&':'', + $method->getName(), + implode(', ', $this->generateArguments($method->getArguments())), + version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType() + ? sprintf(': %s', $method->getReturnType()) + : '' + ); + $php .= $method->getCode()."\n"; + + return $php.'}'; + } + + private function generateArguments(array $arguments) + { + return array_map(function (Node\ArgumentNode $argument) { + $php = ''; + + if ($hint = $argument->getTypeHint()) { + switch ($hint) { + case 'array': + case 'callable': + $php .= $hint; + break; + + case 'string': + case 'int': + case 'float': + case 'bool': + if (version_compare(PHP_VERSION, '7.0', '>=')) { + $php .= $hint; + break; + } + // Fall-through to default case for PHP 5.x + + default: + $php .= '\\'.$hint; + } + } + + $php .= ' '.($argument->isPassedByReference() ? '&' : ''); + + $php .= $argument->isVariadic() ? '...' : ''; + + $php .= '$'.$argument->getName(); + + if ($argument->isOptional() && !$argument->isVariadic()) { + $php .= ' = '.var_export($argument->getDefault(), true); + } + + return $php; + }, $arguments); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +use Prophecy\Exception\Doubler\ClassCreatorException; + +/** + * Class creator. + * Creates specific class in current environment. + * + * @author Konstantin Kudryashov + */ +class ClassCreator +{ + private $generator; + + /** + * Initializes creator. + * + * @param ClassCodeGenerator $generator + */ + public function __construct(ClassCodeGenerator $generator = null) + { + $this->generator = $generator ?: new ClassCodeGenerator; + } + + /** + * Creates class. + * + * @param string $classname + * @param Node\ClassNode $class + * + * @return mixed + * + * @throws \Prophecy\Exception\Doubler\ClassCreatorException + */ + public function create($classname, Node\ClassNode $class) + { + $code = $this->generator->generate($classname, $class); + $return = eval($code); + + if (!class_exists($classname, false)) { + if (count($class->getInterfaces())) { + throw new ClassCreatorException(sprintf( + 'Could not double `%s` and implement interfaces: [%s].', + $class->getParentClass(), implode(', ', $class->getInterfaces()) + ), $class); + } + + throw new ClassCreatorException( + sprintf('Could not double `%s`.', $class->getParentClass()), + $class + ); + } + + return $return; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Doubler\ClassMirrorException; +use ReflectionClass; +use ReflectionMethod; +use ReflectionParameter; + +/** + * Class mirror. + * Core doubler class. Mirrors specific class and/or interfaces into class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassMirror +{ + private static $reflectableMethods = array( + '__construct', + '__destruct', + '__sleep', + '__wakeup', + '__toString', + '__call', + '__invoke' + ); + + /** + * Reflects provided arguments into class node. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return Node\ClassNode + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function reflect(ReflectionClass $class = null, array $interfaces) + { + $node = new Node\ClassNode; + + if (null !== $class) { + if (true === $class->isInterface()) { + throw new InvalidArgumentException(sprintf( + "Could not reflect %s as a class, because it\n". + "is interface - use the second argument instead.", + $class->getName() + )); + } + + $this->reflectClassToNode($class, $node); + } + + foreach ($interfaces as $interface) { + if (!$interface instanceof ReflectionClass) { + throw new InvalidArgumentException(sprintf( + "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". + "a second argument to `ClassMirror::reflect(...)`, but got %s.", + is_object($interface) ? get_class($interface).' class' : gettype($interface) + )); + } + if (false === $interface->isInterface()) { + throw new InvalidArgumentException(sprintf( + "Could not reflect %s as an interface, because it\n". + "is class - use the first argument instead.", + $interface->getName() + )); + } + + $this->reflectInterfaceToNode($interface, $node); + } + + $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface'); + + return $node; + } + + private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node) + { + if (true === $class->isFinal()) { + throw new ClassMirrorException(sprintf( + 'Could not reflect class %s as it is marked final.', $class->getName() + ), $class); + } + + $node->setParentClass($class->getName()); + + foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { + if (false === $method->isProtected()) { + continue; + } + + $this->reflectMethodToNode($method, $node); + } + + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if (0 === strpos($method->getName(), '_') + && !in_array($method->getName(), self::$reflectableMethods)) { + continue; + } + + if (true === $method->isFinal()) { + $node->addUnextendableMethod($method->getName()); + continue; + } + + $this->reflectMethodToNode($method, $node); + } + } + + private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node) + { + $node->addInterface($interface->getName()); + + foreach ($interface->getMethods() as $method) { + $this->reflectMethodToNode($method, $node); + } + } + + private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode) + { + $node = new Node\MethodNode($method->getName()); + + if (true === $method->isProtected()) { + $node->setVisibility('protected'); + } + + if (true === $method->isStatic()) { + $node->setStatic(); + } + + if (true === $method->returnsReference()) { + $node->setReturnsReference(); + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $method->hasReturnType()) { + $returnType = (string) $method->getReturnType(); + $returnTypeLower = strtolower($returnType); + + if ('self' === $returnTypeLower) { + $returnType = $method->getDeclaringClass()->getName(); + } + if ('parent' === $returnTypeLower) { + $returnType = $method->getDeclaringClass()->getParentClass()->getName(); + } + + $node->setReturnType($returnType); + } + + if (is_array($params = $method->getParameters()) && count($params)) { + foreach ($params as $param) { + $this->reflectArgumentToNode($param, $node); + } + } + + $classNode->addMethod($node); + } + + private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode) + { + $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); + $node = new Node\ArgumentNode($name); + + $node->setTypeHint($this->getTypeHint($parameter)); + + if ($this->isVariadic($parameter)) { + $node->setAsVariadic(); + } + + if ($this->hasDefaultValue($parameter)) { + $node->setDefault($this->getDefaultValue($parameter)); + } + + if ($parameter->isPassedByReference()) { + $node->setAsPassedByReference(); + } + + $methodNode->addArgument($node); + } + + private function hasDefaultValue(ReflectionParameter $parameter) + { + if ($this->isVariadic($parameter)) { + return false; + } + + if ($parameter->isDefaultValueAvailable()) { + return true; + } + + return $parameter->isOptional() || $this->isNullable($parameter); + } + + private function getDefaultValue(ReflectionParameter $parameter) + { + if (!$parameter->isDefaultValueAvailable()) { + return null; + } + + return $parameter->getDefaultValue(); + } + + private function getTypeHint(ReflectionParameter $parameter) + { + if (null !== $className = $this->getParameterClassName($parameter)) { + return $className; + } + + if (true === $parameter->isArray()) { + return 'array'; + } + + if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) { + return 'callable'; + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) { + return (string) $parameter->getType(); + } + + return null; + } + + private function isVariadic(ReflectionParameter $parameter) + { + return PHP_VERSION_ID >= 50600 && $parameter->isVariadic(); + } + + private function isNullable(ReflectionParameter $parameter) + { + return $parameter->allowsNull() && null !== $this->getTypeHint($parameter); + } + + private function getParameterClassName(ReflectionParameter $parameter) + { + try { + return $parameter->getClass() ? $parameter->getClass()->getName() : null; + } catch (\ReflectionException $e) { + preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches); + + return isset($matches[1]) ? $matches[1] : null; + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\Doubler\MethodNotExtendableException; +use Prophecy\Exception\InvalidArgumentException; + +/** + * Class node. + * + * @author Konstantin Kudryashov + */ +class ClassNode +{ + private $parentClass = 'stdClass'; + private $interfaces = array(); + private $properties = array(); + private $unextendableMethods = array(); + + /** + * @var MethodNode[] + */ + private $methods = array(); + + public function getParentClass() + { + return $this->parentClass; + } + + /** + * @param string $class + */ + public function setParentClass($class) + { + $this->parentClass = $class ?: 'stdClass'; + } + + /** + * @return string[] + */ + public function getInterfaces() + { + return $this->interfaces; + } + + /** + * @param string $interface + */ + public function addInterface($interface) + { + if ($this->hasInterface($interface)) { + return; + } + + array_unshift($this->interfaces, $interface); + } + + /** + * @param string $interface + * + * @return bool + */ + public function hasInterface($interface) + { + return in_array($interface, $this->interfaces); + } + + public function getProperties() + { + return $this->properties; + } + + public function addProperty($name, $visibility = 'public') + { + $visibility = strtolower($visibility); + + if (!in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(sprintf( + '`%s` property visibility is not supported.', $visibility + )); + } + + $this->properties[$name] = $visibility; + } + + /** + * @return MethodNode[] + */ + public function getMethods() + { + return $this->methods; + } + + public function addMethod(MethodNode $method) + { + if (!$this->isExtendable($method->getName())){ + $message = sprintf( + 'Method `%s` is not extendable, so can not be added.', $method->getName() + ); + throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); + } + $this->methods[$method->getName()] = $method; + } + + public function removeMethod($name) + { + unset($this->methods[$name]); + } + + /** + * @param string $name + * + * @return MethodNode|null + */ + public function getMethod($name) + { + return $this->hasMethod($name) ? $this->methods[$name] : null; + } + + /** + * @param string $name + * + * @return bool + */ + public function hasMethod($name) + { + return isset($this->methods[$name]); + } + + /** + * @return string[] + */ + public function getUnextendableMethods() + { + return $this->unextendableMethods; + } + + /** + * @param string $unextendableMethod + */ + public function addUnextendableMethod($unextendableMethod) + { + if (!$this->isExtendable($unextendableMethod)){ + return; + } + $this->unextendableMethods[] = $unextendableMethod; + } + + /** + * @param string $method + * @return bool + */ + public function isExtendable($method) + { + return !in_array($method, $this->unextendableMethods); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Method node. + * + * @author Konstantin Kudryashov + */ +class MethodNode +{ + private $name; + private $code; + private $visibility = 'public'; + private $static = false; + private $returnsReference = false; + private $returnType; + + /** + * @var ArgumentNode[] + */ + private $arguments = array(); + + /** + * @param string $name + * @param string $code + */ + public function __construct($name, $code = null) + { + $this->name = $name; + $this->code = $code; + } + + public function getVisibility() + { + return $this->visibility; + } + + /** + * @param string $visibility + */ + public function setVisibility($visibility) + { + $visibility = strtolower($visibility); + + if (!in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(sprintf( + '`%s` method visibility is not supported.', $visibility + )); + } + + $this->visibility = $visibility; + } + + public function isStatic() + { + return $this->static; + } + + public function setStatic($static = true) + { + $this->static = (bool) $static; + } + + public function returnsReference() + { + return $this->returnsReference; + } + + public function setReturnsReference() + { + $this->returnsReference = true; + } + + public function getName() + { + return $this->name; + } + + public function addArgument(ArgumentNode $argument) + { + $this->arguments[] = $argument; + } + + /** + * @return ArgumentNode[] + */ + public function getArguments() + { + return $this->arguments; + } + + public function hasReturnType() + { + return null !== $this->returnType; + } + + /** + * @param string $type + */ + public function setReturnType($type = null) + { + switch ($type) { + case '': + $this->returnType = null; + break; + + case 'string'; + case 'float': + case 'int': + case 'bool': + case 'array': + case 'callable': + $this->returnType = $type; + break; + + case 'double': + case 'real': + $this->returnType = 'float'; + break; + + case 'boolean': + $this->returnType = 'bool'; + break; + + case 'integer': + $this->returnType = 'int'; + break; + + default: + $this->returnType = '\\' . ltrim($type, '\\'); + } + } + + public function getReturnType() + { + return $this->returnType; + } + + /** + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + + public function getCode() + { + if ($this->returnsReference) + { + return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; + } + + return (string) $this->code; + } + + public function useParentCode() + { + $this->code = sprintf( + 'return parent::%s(%s);', $this->getName(), implode(', ', + array_map(array($this, 'generateArgument'), $this->arguments) + ) + ); + } + + private function generateArgument(ArgumentNode $arg) + { + $argument = '$'.$arg->getName(); + + if ($arg->isVariadic()) { + $argument = '...'.$argument; + } + + return $argument; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +/** + * Argument node. + * + * @author Konstantin Kudryashov + */ +class ArgumentNode +{ + private $name; + private $typeHint; + private $default; + private $optional = false; + private $byReference = false; + private $isVariadic = false; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getTypeHint() + { + return $this->typeHint; + } + + public function setTypeHint($typeHint = null) + { + $this->typeHint = $typeHint; + } + + public function hasDefault() + { + return $this->isOptional() && !$this->isVariadic(); + } + + public function getDefault() + { + return $this->default; + } + + public function setDefault($default = null) + { + $this->optional = true; + $this->default = $default; + } + + public function isOptional() + { + return $this->optional; + } + + public function setAsPassedByReference($byReference = true) + { + $this->byReference = $byReference; + } + + public function isPassedByReference() + { + return $this->byReference; + } + + public function setAsVariadic($isVariadic = true) + { + $this->isVariadic = $isVariadic; + } + + public function isVariadic() + { + return $this->isVariadic; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +/** + * Reflection interface. + * All reflected classes implement this interface. + * + * @author Konstantin Kudryashov + */ +interface ReflectionInterface +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use Doctrine\Instantiator\Instantiator; +use Prophecy\Doubler\ClassPatch\ClassPatchInterface; +use Prophecy\Doubler\Generator\ClassMirror; +use Prophecy\Doubler\Generator\ClassCreator; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; + +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class Doubler +{ + private $mirror; + private $creator; + private $namer; + + /** + * @var ClassPatchInterface[] + */ + private $patches = array(); + + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * Initializes doubler. + * + * @param ClassMirror $mirror + * @param ClassCreator $creator + * @param NameGenerator $namer + */ + public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, + NameGenerator $namer = null) + { + $this->mirror = $mirror ?: new ClassMirror; + $this->creator = $creator ?: new ClassCreator; + $this->namer = $namer ?: new NameGenerator; + } + + /** + * Returns list of registered class patches. + * + * @return ClassPatchInterface[] + */ + public function getClassPatches() + { + return $this->patches; + } + + /** + * Registers new class patch. + * + * @param ClassPatchInterface $patch + */ + public function registerClassPatch(ClassPatchInterface $patch) + { + $this->patches[] = $patch; + + @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { + return $patch2->getPriority() - $patch1->getPriority(); + }); + } + + /** + * Creates double from specific class or/and list of interfaces. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces Array of ReflectionClass instances + * @param array $args Constructor arguments + * + * @return DoubleInterface + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function double(ReflectionClass $class = null, array $interfaces, array $args = null) + { + foreach ($interfaces as $interface) { + if (!$interface instanceof ReflectionClass) { + throw new InvalidArgumentException(sprintf( + "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". + "a second argument to `Doubler::double(...)`, but got %s.", + is_object($interface) ? get_class($interface).' class' : gettype($interface) + )); + } + } + + $classname = $this->createDoubleClass($class, $interfaces); + $reflection = new ReflectionClass($classname); + + if (null !== $args) { + return $reflection->newInstanceArgs($args); + } + if ((null === $constructor = $reflection->getConstructor()) + || ($constructor->isPublic() && !$constructor->isFinal())) { + return $reflection->newInstance(); + } + + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); + } + + return $this->instantiator->instantiate($classname); + } + + /** + * Creates double class and returns its FQN. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $name = $this->namer->name($class, $interfaces); + $node = $this->mirror->reflect($class, $interfaces); + + foreach ($this->patches as $patch) { + if ($patch->supports($node)) { + $patch->apply($node); + } + } + + $this->creator->create($name, $node); + + return $name; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use ReflectionClass; + +/** + * Name generator. + * Generates classname for double. + * + * @author Konstantin Kudryashov + */ +class NameGenerator +{ + private static $counter = 1; + + /** + * Generates name. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + public function name(ReflectionClass $class = null, array $interfaces) + { + $parts = array(); + + if (null !== $class) { + $parts[] = $class->getName(); + } else { + foreach ($interfaces as $interface) { + $parts[] = $interface->getShortName(); + } + } + + if (!count($parts)) { + $parts[] = 'stdClass'; + } + + return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use Prophecy\Exception\Doubler\DoubleException; +use Prophecy\Exception\Doubler\ClassNotFoundException; +use Prophecy\Exception\Doubler\InterfaceNotFoundException; +use ReflectionClass; + +/** + * Lazy double. + * Gives simple interface to describe double before creating it. + * + * @author Konstantin Kudryashov + */ +class LazyDouble +{ + private $doubler; + private $class; + private $interfaces = array(); + private $arguments = null; + private $double; + + /** + * Initializes lazy double. + * + * @param Doubler $doubler + */ + public function __construct(Doubler $doubler) + { + $this->doubler = $doubler; + } + + /** + * Tells doubler to use specific class as parent one for double. + * + * @param string|ReflectionClass $class + * + * @throws \Prophecy\Exception\Doubler\ClassNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException + */ + public function setParentClass($class) + { + if (null !== $this->double) { + throw new DoubleException('Can not extend class with already instantiated double.'); + } + + if (!$class instanceof ReflectionClass) { + if (!class_exists($class)) { + throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class); + } + + $class = new ReflectionClass($class); + } + + $this->class = $class; + } + + /** + * Tells doubler to implement specific interface with double. + * + * @param string|ReflectionClass $interface + * + * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException + */ + public function addInterface($interface) + { + if (null !== $this->double) { + throw new DoubleException( + 'Can not implement interface with already instantiated double.' + ); + } + + if (!$interface instanceof ReflectionClass) { + if (!interface_exists($interface)) { + throw new InterfaceNotFoundException( + sprintf('Interface %s not found.', $interface), + $interface + ); + } + + $interface = new ReflectionClass($interface); + } + + $this->interfaces[] = $interface; + } + + /** + * Sets constructor arguments. + * + * @param array $arguments + */ + public function setArguments(array $arguments = null) + { + $this->arguments = $arguments; + } + + /** + * Creates double instance or returns already created one. + * + * @return DoubleInterface + */ + public function getInstance() + { + if (null === $this->double) { + if (null !== $this->arguments) { + return $this->double = $this->doubler->double( + $this->class, $this->interfaces, $this->arguments + ); + } + + $this->double = $this->doubler->double($this->class, $this->interfaces); + } + + return $this->double; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use ReflectionClass; + +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class CachedDoubler extends Doubler +{ + private $classes = array(); + + /** + * {@inheritdoc} + */ + public function registerClassPatch(ClassPatch\ClassPatchInterface $patch) + { + $this->classes[] = array(); + + parent::registerClassPatch($patch); + } + + /** + * {@inheritdoc} + */ + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $classId = $this->generateClassId($class, $interfaces); + if (isset($this->classes[$classId])) { + return $this->classes[$classId]; + } + + return $this->classes[$classId] = parent::createDoubleClass($class, $interfaces); + } + + /** + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + private function generateClassId(ReflectionClass $class = null, array $interfaces) + { + $parts = array(); + if (null !== $class) { + $parts[] = $class->getName(); + } + foreach ($interfaces as $interface) { + $parts[] = $interface->getName(); + } + sort($parts); + + return md5(implode('', $parts)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +/** + * Core double interface. + * All doubled classes will implement this one. + * + * @author Konstantin Kudryashov + */ +interface DoubleInterface +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Remove method functionality from the double which will clash with php keywords. + * + * @author Milan Magudia + */ +class KeywordPatch implements ClassPatchInterface +{ + /** + * Support any class + * + * @param ClassNode $node + * + * @return boolean + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Remove methods that clash with php keywords + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $methodNames = array_keys($node->getMethods()); + $methodsToRemove = array_intersect($methodNames, $this->getKeywords()); + foreach ($methodsToRemove as $methodName) { + $node->removeMethod($methodName); + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() { + return 49; + } + + /** + * Returns array of php keywords. + * + * @return array + */ + private function getKeywords() { + + return array( + '__halt_compiler', + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'callable', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'eval', + 'exit', + 'extends', + 'final', + 'finally', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'namespace', + 'new', + 'or', + 'print', + 'private', + 'protected', + 'public', + 'require', + 'require_once', + 'return', + 'static', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Exception patch for HHVM to remove the stubs from special methods + * + * @author Christophe Coevoet + */ +class HhvmExceptionPatch implements ClassPatchInterface +{ + /** + * Supports exceptions on HHVM. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (!defined('HHVM_VERSION')) { + return false; + } + + return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception'); + } + + /** + * Removes special exception static methods from the doubled methods. + * + * @param ClassNode $node + * + * @return void + */ + public function apply(ClassNode $node) + { + if ($node->hasMethod('setTraceOptions')) { + $node->getMethod('setTraceOptions')->useParentCode(); + } + if ($node->hasMethod('getTraceOptions')) { + $node->getMethod('getTraceOptions')->useParentCode(); + } + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return -50; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\Doubler\Generator\Node\ArgumentNode; + +/** + * Add Prophecy functionality to the double. + * This is a core class patch for Prophecy. + * + * @author Konstantin Kudryashov + */ +class ProphecySubjectPatch implements ClassPatchInterface +{ + /** + * Always returns true. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Apply Prophecy functionality to class node. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface'); + $node->addProperty('objectProphecy', 'private'); + + foreach ($node->getMethods() as $name => $method) { + if ('__construct' === strtolower($name)) { + continue; + } + + $method->setCode( + 'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' + ); + } + + $prophecySetter = new MethodNode('setProphecy'); + $prophecyArgument = new ArgumentNode('prophecy'); + $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface'); + $prophecySetter->addArgument($prophecyArgument); + $prophecySetter->setCode('$this->objectProphecy = $prophecy;'); + + $prophecyGetter = new MethodNode('getProphecy'); + $prophecyGetter->setCode('return $this->objectProphecy;'); + + if ($node->hasMethod('__call')) { + $__call = $node->getMethod('__call'); + } else { + $__call = new MethodNode('__call'); + $__call->addArgument(new ArgumentNode('name')); + $__call->addArgument(new ArgumentNode('arguments')); + + $node->addMethod($__call); + } + + $__call->setCode(<<getProphecy(), func_get_arg(0) +); +PHP + ); + + $node->addMethod($prophecySetter); + $node->addMethod($prophecyGetter); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 0; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Traversable interface patch. + * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. + * + * @author Konstantin Kudryashov + */ +class TraversablePatch implements ClassPatchInterface +{ + /** + * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (in_array('Iterator', $node->getInterfaces())) { + return false; + } + if (in_array('IteratorAggregate', $node->getInterfaces())) { + return false; + } + + foreach ($node->getInterfaces() as $interface) { + if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) { + continue; + } + if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) { + continue; + } + if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) { + continue; + } + + return true; + } + + return false; + } + + /** + * Forces class to implement Iterator interface. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $node->addInterface('Iterator'); + + $node->addMethod(new MethodNode('current')); + $node->addMethod(new MethodNode('key')); + $node->addMethod(new MethodNode('next')); + $node->addMethod(new MethodNode('rewind')); + $node->addMethod(new MethodNode('valid')); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 100; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Disable constructor. + * Makes all constructor arguments optional. + * + * @author Konstantin Kudryashov + */ +class DisableConstructorPatch implements ClassPatchInterface +{ + /** + * Checks if class has `__construct` method. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Makes all class constructor arguments optional. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + if (!$node->hasMethod('__construct')) { + $node->addMethod(new MethodNode('__construct', '')); + + return; + } + + $constructor = $node->getMethod('__construct'); + foreach ($constructor->getArguments() as $argument) { + $argument->setDefault(null); + } + + $constructor->setCode(<< + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Class patch interface. + * Class patches extend doubles functionality or help + * Prophecy to avoid some internal PHP bugs. + * + * @author Konstantin Kudryashov + */ +interface ClassPatchInterface +{ + /** + * Checks if patch supports specific class node. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node); + + /** + * Applies patch to the specific class node. + * + * @param ClassNode $node + * @return void + */ + public function apply(ClassNode $node); + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use phpDocumentor\Reflection\DocBlock; +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Discover Magical API using "@method" PHPDoc format. + * + * @author Thomas Tourlourat + */ +class MagicCallPatch implements ClassPatchInterface +{ + /** + * Support any class + * + * @param ClassNode $node + * + * @return boolean + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Discover Magical API + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $parentClass = $node->getParentClass(); + $reflectionClass = new \ReflectionClass($parentClass); + + $phpdoc = new DocBlock($reflectionClass->getDocComment()); + + $tagList = $phpdoc->getTagsByName('method'); + + $interfaces = $reflectionClass->getInterfaces(); + foreach($interfaces as $interface) { + $phpdoc = new DocBlock($interface); + $tagList = array_merge($tagList, $phpdoc->getTagsByName('method')); + } + + foreach($tagList as $tag) { + $methodName = $tag->getMethodName(); + + if (!$reflectionClass->hasMethod($methodName)) { + $methodNode = new MethodNode($tag->getMethodName()); + $methodNode->setStatic($tag->isStatic()); + + $node->addMethod($methodNode); + } + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return integer Priority number (higher - earlier) + */ + public function getPriority() + { + return 50; + } +} + + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * ReflectionClass::newInstance patch. + * Makes first argument of newInstance optional, since it works but signature is misleading + * + * @author Florian Klein + */ +class ReflectionClassNewInstancePatch implements ClassPatchInterface +{ + /** + * Supports ReflectionClass + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return 'ReflectionClass' === $node->getParentClass(); + } + + /** + * Updates newInstance's first argument to make it optional + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + foreach ($node->getMethod('newInstance')->getArguments() as $argument) { + $argument->setDefault(null); + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher = earlier) + */ + public function getPriority() + { + return 50; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * SplFileInfo patch. + * Makes SplFileInfo and derivative classes usable with Prophecy. + * + * @author Konstantin Kudryashov + */ +class SplFileInfoPatch implements ClassPatchInterface +{ + /** + * Supports everything that extends SplFileInfo. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (null === $node->getParentClass()) { + return false; + } + + return 'SplFileInfo' === $node->getParentClass() + || is_subclass_of($node->getParentClass(), 'SplFileInfo') + ; + } + + /** + * Updated constructor code to call parent one with dummy file argument. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + if ($node->hasMethod('__construct')) { + $constructor = $node->getMethod('__construct'); + } else { + $constructor = new MethodNode('__construct'); + $node->addMethod($constructor); + } + + if ($this->nodeIsDirectoryIterator($node)) { + $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); + return; + } + + $constructor->useParentCode(); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 50; + } + + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsDirectoryIterator(ClassNode $node) + { + $parent = $node->getParentClass(); + return 'DirectoryIterator' === $parent + || is_subclass_of($parent, 'DirectoryIterator'); + } +} +Copyright (c) 2013 Konstantin Kudryashov + Marcello Duarte + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * TestCase class that uses Selenium 2 + * (WebDriver API and JsonWire protocol) to provide + * the functionality required for web testing. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + * @method void acceptAlert() Press OK on an alert, or confirms a dialog + * @method mixed alertText() alertText($value = NULL) Gets the alert dialog text, or sets the text for a prompt dialog + * @method void back() + * @method \PHPUnit_Extensions_Selenium2TestCase_Element byClassName() byClassName($value) + * @method \PHPUnit_Extensions_Selenium2TestCase_Element byCssSelector() byCssSelector($value) + * @method \PHPUnit_Extensions_Selenium2TestCase_Element byId() byId($value) + * @method \PHPUnit_Extensions_Selenium2TestCase_Element byLinkText() byLinkText($value) + * @method \PHPUnit_Extensions_Selenium2TestCase_Element byName() byName($value) + * @method \PHPUnit_Extensions_Selenium2TestCase_Element byTag() byTag($value) + * @method \PHPUnit_Extensions_Selenium2TestCase_Element byXPath() byXPath($value) + * @method void click() click(int $button = 0) Click any mouse button (at the coordinates set by the last moveto command). + * @method void clickOnElement() clickOnElement($id) + * @method string currentScreenshot() BLOB of the image file + * @method void dismissAlert() Press Cancel on an alert, or does not confirm a dialog + * @method void doubleclick() Double clicks (at the coordinates set by the last moveto command). + * @method \PHPUnit_Extensions_Selenium2TestCase_Element element() element(\PHPUnit_Extensions_Selenium2TestCase_ElementCriteria $criteria) Retrieves an element + * @method array elements() elements(\PHPUnit_Extensions_Selenium2TestCase_ElementCriteria $criteria) Retrieves an array of Element instances + * @method string execute() execute($javaScriptCode) Injects arbitrary JavaScript in the page and returns the last + * @method string executeAsync() executeAsync($javaScriptCode) Injects arbitrary JavaScript and wait for the callback (last element of arguments) to be called + * @method void forward() + * @method void frame() frame(mixed $element) Changes the focus to a frame in the page (by frameCount of type int, htmlId of type string, htmlName of type string or element of type \PHPUnit_Extensions_Selenium2TestCase_Element) + * @method void moveto() moveto(\PHPUnit_Extensions_Selenium2TestCase_Element $element) Move the mouse by an offset of the specificed element. + * @method void refresh() + * @method \PHPUnit_Extensions_Selenium2TestCase_Element_Select select() select($element) + * @method string source() Returns the HTML source of the page + * @method \PHPUnit_Extensions_Selenium2TestCase_Session_Timeouts timeouts() + * @method string title() + * @method void|string url() url($url = NULL) + * @method PHPUnit_Extensions_Selenium2TestCase_ElementCriteria using() using($strategy) Factory Method for Criteria objects + * @method void window() window($name) Changes the focus to another window + * @method string windowHandle() Retrieves the current window handle + * @method string windowHandles() Retrieves a list of all available window handles + * @method string keys() Send a sequence of key strokes to the active element. + * @method string file($file_path) Upload a local file. Returns the fully qualified path to the transferred file. + * @method array log(string $type) Get the log for a given log type. Log buffer is reset after each request. + * @method array logTypes() Get available log types. + * @method void closeWindow() Close the current window. + * @method void close() Close the current window and clear session data. + * @method \PHPUnit_Extensions_Selenium2TestCase_Element active() Get the element on the page that currently has focus. + */ +abstract class PHPUnit_Extensions_Selenium2TestCase extends PHPUnit_Framework_TestCase +{ + const VERSION = '1.4.2'; + + /** + * @var string override to provide code coverage data from the server + */ + protected $coverageScriptUrl; + + /** + * @var PHPUnit_Extensions_Selenium2TestCase_Session + */ + private $session; + + /** + * @var array + */ + private $parameters; + + /** + * @var PHPUnit_Extensions_Selenium2TestCase_SessionStrategy + */ + protected static $sessionStrategy; + + /** + * @var PHPUnit_Extensions_Selenium2TestCase_SessionStrategy + */ + protected static $browserSessionStrategy; + + /** + * @var PHPUnit_Extensions_Selenium2TestCase_SessionStrategy + */ + protected $localSessionStrategy; + + /** + * @var array + */ + private static $lastBrowserParams; + + /** + * @var string + */ + private $testId; + + /** + * @var boolean + */ + private $collectCodeCoverageInformation; + + /** + * @var PHPUnit_Extensions_Selenium2TestCase_KeysHolder + */ + private $keysHolder; + + /** + * @param boolean + */ + public static function shareSession($shareSession) + { + if (!is_bool($shareSession)) { + throw new InvalidArgumentException("The shared session support can only be switched on or off."); + } + if (!$shareSession) { + self::$sessionStrategy = self::defaultSessionStrategy(); + } else { + self::$sessionStrategy = new PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Shared(self::defaultSessionStrategy()); + } + } + + private static function sessionStrategy() + { + if (!self::$sessionStrategy) { + self::$sessionStrategy = self::defaultSessionStrategy(); + } + return self::$sessionStrategy; + } + + private static function defaultSessionStrategy() + { + return new PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Isolated; + } + + public function __construct($name = NULL, array $data = array(), $dataName = '') + { + parent::__construct($name, $data, $dataName); + $this->parameters = array( + 'host' => 'localhost', + 'port' => 4444, + 'browser' => NULL, + 'browserName' => NULL, + 'desiredCapabilities' => array(), + 'seleniumServerRequestsTimeout' => 60 + ); + + $this->keysHolder = new PHPUnit_Extensions_Selenium2TestCase_KeysHolder(); + } + + public function setupSpecificBrowser($params) + { + $this->setUpSessionStrategy($params); + $params = array_merge($this->parameters, $params); + $this->setHost($params['host']); + $this->setPort($params['port']); + $this->setBrowser($params['browserName']); + $this->parameters['browser'] = $params['browser']; + $this->setDesiredCapabilities($params['desiredCapabilities']); + $this->setSeleniumServerRequestsTimeout( + $params['seleniumServerRequestsTimeout']); + } + + protected function setUpSessionStrategy($params) + { + // This logic enables us to have a session strategy reused for each + // item in self::$browsers. We don't want them both to share one + // and we don't want each test for a specific browser to have a + // new strategy + if ($params == self::$lastBrowserParams) { + // do nothing so we use the same session strategy for this + // browser + } elseif (isset($params['sessionStrategy'])) { + $strat = $params['sessionStrategy']; + if ($strat != "isolated" && $strat != "shared") { + throw new InvalidArgumentException("Session strategy must be either 'isolated' or 'shared'"); + } elseif ($strat == "isolated") { + self::$browserSessionStrategy = new PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Isolated; + } else { + self::$browserSessionStrategy = new PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Shared(self::defaultSessionStrategy()); + } + } else { + self::$browserSessionStrategy = self::defaultSessionStrategy(); + } + self::$lastBrowserParams = $params; + $this->localSessionStrategy = self::$browserSessionStrategy; + + } + + private function getStrategy() + { + if ($this->localSessionStrategy) { + return $this->localSessionStrategy; + } else { + return self::sessionStrategy(); + } + } + + public function prepareSession() + { + try { + if (!$this->session) { + $this->session = $this->getStrategy()->session($this->parameters); + } + } catch (PHPUnit_Extensions_Selenium2TestCase_NoSeleniumException $e) { + $this->markTestSkipped("The Selenium Server is not active on host {$this->parameters['host']} at port {$this->parameters['port']}."); + } + return $this->session; + } + + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + $this->testId = get_class($this) . '__' . $this->getName(); + + if ($result === NULL) { + $result = $this->createResult(); + } + + $this->collectCodeCoverageInformation = $result->getCollectCodeCoverageInformation(); + + parent::run($result); + + if ($this->collectCodeCoverageInformation) { + $coverage = new PHPUnit_Extensions_SeleniumCommon_RemoteCoverage( + $this->coverageScriptUrl, + $this->testId + ); + $result->getCodeCoverage()->append( + $coverage->get(), $this + ); + } + + // do not call this before to give the time to the Listeners to run + $this->getStrategy()->endOfTest($this->session); + + return $result; + } + + /** + * @throws RuntimeException + */ + protected function runTest() + { + $this->prepareSession(); + + $thrownException = NULL; + + if ($this->collectCodeCoverageInformation) { + $this->url($this->coverageScriptUrl); // phpunit_coverage.php won't do anything if the cookie isn't set, which is exactly what we want + $this->session->cookie()->add('PHPUNIT_SELENIUM_TEST_ID', $this->testId)->set(); + } + + try { + $this->setUpPage(); + $result = parent::runTest(); + + if (!empty($this->verificationErrors)) { + $this->fail(implode("\n", $this->verificationErrors)); + } + } catch (Exception $e) { + $thrownException = $e; + } + + if ($this->collectCodeCoverageInformation) { + $this->session->cookie()->remove('PHPUNIT_SELENIUM_TEST_ID'); + } + + if (NULL !== $thrownException) { + throw $thrownException; + } + + return $result; + } + + + public static function suite($className) + { + return PHPUnit_Extensions_SeleniumTestSuite::fromTestCaseClass($className); + } + + public function onNotSuccessfulTest(Exception $e) + { + $this->getStrategy()->notSuccessfulTest(); + parent::onNotSuccessfulTest($e); + } + + /** + * Delegate method calls to the Session. + * + * @param string $command + * @param array $arguments + * @return mixed + */ + public function __call($command, $arguments) + { + if ($this->session === NULL) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception("There is currently no active session to execute the '$command' command. You're probably trying to set some option in setUp() with an incorrect setter name. You may consider using setUpPage() instead."); + } + $result = call_user_func_array( + array($this->session, $command), $arguments + ); + + return $result; + } + + /** + * @param string $host + * @throws InvalidArgumentException + */ + public function setHost($host) + { + if (!is_string($host)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->parameters['host'] = $host; + } + + public function getHost() + { + return $this->parameters['host']; + } + + /** + * @param integer $port + * @throws InvalidArgumentException + */ + public function setPort($port) + { + if (!is_int($port)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->parameters['port'] = $port; + } + + public function getPort() + { + return $this->parameters['port']; + } + + /** + * @param string $browser + * @throws InvalidArgumentException + */ + public function setBrowser($browserName) + { + if (!is_string($browserName)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->parameters['browserName'] = $browserName; + } + + public function getBrowser() + { + return $this->parameters['browserName']; + } + + /** + * @param string $browserUrl + * @throws InvalidArgumentException + */ + public function setBrowserUrl($browserUrl) + { + if (!is_string($browserUrl)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->parameters['browserUrl'] = new PHPUnit_Extensions_Selenium2TestCase_URL($browserUrl); + } + + public function getBrowserUrl() + { + if (isset($this->parameters['browserUrl'])) { + return $this->parameters['browserUrl']; + } + return ''; + } + + /** + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol + */ + public function setDesiredCapabilities(array $capabilities) + { + $this->parameters['desiredCapabilities'] = $capabilities; + } + + + public function getDesiredCapabilities() + { + return $this->parameters['desiredCapabilities']; + } + + /** + * @param int $timeout seconds + */ + public function setSeleniumServerRequestsTimeout($timeout) + { + $this->parameters['seleniumServerRequestsTimeout'] = $timeout; + } + + public function getSeleniumServerRequestsTimeout() + { + return $this->parameters['seleniumServerRequestsTimeout']; + } + + /** + * Get test id (generated internally) + * @return string + */ + public function getTestId() + { + return $this->testId; + } + + /** + * Get Selenium2 current session id + * @return string + */ + public function getSessionId() + { + if ($this->session) { + return $this->session->id(); + } + return FALSE; + } + + /** + * Wait until callback isn't null or timeout occurs + * + * @param $callback + * @param null $timeout + * @return mixed + */ + public function waitUntil($callback, $timeout = NULL) + { + $waitUntil = new PHPUnit_Extensions_Selenium2TestCase_WaitUntil($this); + return $waitUntil->run($callback, $timeout); + } + + /** + * Sends a special key + * Deprecated due to issues with IE webdriver. Use keys() method instead + * @deprecated + * @param string $name + * @throws PHPUnit_Extensions_Selenium2TestCase_Exception + * @see PHPUnit_Extensions_Selenium2TestCase_KeysHolder + */ + public function keysSpecial($name) + { + $names = explode(',', $name); + + foreach ($names as $key) { + $this->keys($this->keysHolder->specialKey(trim($key))); + } + } + + /** + * setUp method that is called after the session has been prepared. + * It is possible to use session-specific commands like url() here. + */ + public function setUpPage() + { + + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * TestSuite class for a set of tests from a single Testcase Class + * executed with a particular browser. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_SeleniumBrowserSuite extends PHPUnit_Framework_TestSuite +{ + /** + * Overriding the default: Selenium suites are always built from a TestCase class. + * @var boolean + */ + protected $testCase = TRUE; + + public function addTestMethod(ReflectionClass $class, ReflectionMethod $method) + { + return parent::addTestMethod($class, $method); + } + + public static function fromClassAndBrowser($className, array $browser) + { + $browserSuite = new self(); + if (isset($browser['browserName'])) { + $name = $browser['browserName']; + } else if (isset($browser['name'])) { + $name = $browser['name']; + } else { + $name = $browser['browser']; + } + $browserSuite->setName($className . ': ' . $name); + return $browserSuite; + } + + public function setupSpecificBrowser(array $browser) + { + $this->browserOnAllTests($this, $browser); + } + + private function browserOnAllTests(PHPUnit_Framework_TestSuite $suite, array $browser) + { + foreach ($suite->tests() as $test) { + if ($test instanceof PHPUnit_Framework_TestSuite) { + $this->browserOnAllTests($test, $browser); + } else { + $test->setupSpecificBrowser($browser); + } + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.5 + */ + +/** + * Gets or sets an attribute of an object. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.5 + */ +class PHPUnit_Extensions_Selenium2TestCase_StateCommand + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.12 + */ + +/** + * Class-mapper, that converts requested special key into correspondent Unicode character + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.12 + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value + */ +class PHPUnit_Extensions_Selenium2TestCase_KeysHolder +{ + private $_keys = array( + 'null' => "\xEE\x80\x80", + 'cancel' => "\xEE\x80\x81", + 'help' => "\xEE\x80\x82", + 'backspace' => "\xEE\x80\x83", + 'tab' => "\xEE\x80\x84", + 'clear' => "\xEE\x80\x85", + 'return' => "\xEE\x80\x86", + 'enter' => "\xEE\x80\x87", + 'shift' => "\xEE\x80\x88", + 'control' => "\xEE\x80\x89", + 'alt' => "\xEE\x80\x8A", + 'pause' => "\xEE\x80\x8B", + 'escape' => "\xEE\x80\x8C", + 'space' => "\xEE\x80\x8D", + 'pageup' => "\xEE\x80\x8E", + 'pagedown' => "\xEE\x80\x8F", + 'end' => "\xEE\x80\x90", + 'home' => "\xEE\x80\x91", + 'left' => "\xEE\x80\x92", + 'up' => "\xEE\x80\x93", + 'right' => "\xEE\x80\x94", + 'down' => "\xEE\x80\x95", + 'insert' => "\xEE\x80\x96", + 'delete' => "\xEE\x80\x97", + 'semicolon' => "\xEE\x80\x98", + 'equals' => "\xEE\x80\x99", + 'numpad0' => "\xEE\x80\x9A", + 'numpad1' => "\xEE\x80\x9B", + 'numpad2' => "\xEE\x80\x9C", + 'numpad3' => "\xEE\x80\x9D", + 'numpad4' => "\xEE\x80\x9E", + 'numpad5' => "\xEE\x80\x9F", + 'numpad6' => "\xEE\x80\xA0", + 'numpad7' => "\xEE\x80\xA1", + 'numpad8' => "\xEE\x80\xA2", + 'numpad9' => "\xEE\x80\xA3", + 'multiply' => "\xEE\x80\xA4", + 'add' => "\xEE\x80\xA5", + 'separator' => "\xEE\x80\xA6", + 'subtract' => "\xEE\x80\xA7", + 'decimal' => "\xEE\x80\xA8", + 'divide' => "\xEE\x80\xA9", + 'f1' => "\xEE\x80\xB1", + 'f2' => "\xEE\x80\xB2", + 'f3' => "\xEE\x80\xB3", + 'f4' => "\xEE\x80\xB4", + 'f5' => "\xEE\x80\xB5", + 'f6' => "\xEE\x80\xB6", + 'f7' => "\xEE\x80\xB7", + 'f8' => "\xEE\x80\xB8", + 'f9' => "\xEE\x80\xB9", + 'f10' => "\xEE\x80\xBA", + 'f11' => "\xEE\x80\xBB", + 'f12' => "\xEE\x80\xBC", + 'command' => "\xEE\x80\xBD", + ); + + public function specialKey($name) + { + $normalizedName = strtolower($name); + + if (!isset($this->_keys[$normalizedName])) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception("There is no special key '$name' defined"); + } + + return $this->_keys[$normalizedName]; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Conditions for selecting a DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCriteria extends ArrayObject +{ + public function __construct($strategy) + { + $this['using'] = $strategy; + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_ElementCriteria + */ + public function value($searchTarget) + { + $this['value'] = $searchTarget; + return $this; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Object representing elements, or everything that may have subcommands. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +abstract class PHPUnit_Extensions_Selenium2TestCase_CommandsHolder +{ + /** + * @var PHPUnit_Extensions_Selenium2TestCase_Driver + */ + protected $driver; + + /** + * @var string the API URL for this element, + */ + protected $url; + + /** + * @var array instances of + * PHPUnit_Extensions_Selenium2TestCase_ElementCommand + */ + protected $commands; + + public function __construct($driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $this->driver = $driver; + $this->url = $url; + $this->commands = array(); + foreach ($this->initCommands() as $commandName => $handler) { + if (is_string($handler)) { + $this->commands[$commandName] = $this->factoryMethod($handler); + } else if (is_callable($handler)) { + $this->commands[$commandName] = $handler; + } else { + throw new InvalidArgumentException("Command $commandName is not configured correctly."); + } + } + } + + /** + * @return array class names, or + * callables of the form function($parameter, $commandUrl) + */ + protected abstract function initCommands(); + + public function __call($commandName, $arguments) + { + $jsonParameters = $this->extractJsonParameters($arguments); + $response = $this->driver->execute($this->newCommand($commandName, $jsonParameters)); + return $response->getValue(); + } + + protected function postCommand($name, PHPUnit_Extensions_Selenium2TestCase_ElementCriteria $criteria) + { + $response = $this->driver->curl('POST', + $this->url->addCommand($name), + $criteria->getArrayCopy()); + return $response->getValue(); + } + + /** + * @params string $commandClass a class name, descending from + PHPUnit_Extensions_Selenium2TestCase_Command + * @return callable + */ + private function factoryMethod($commandClass) + { + return function($jsonParameters, $url) use ($commandClass) { + return new $commandClass($jsonParameters, $url); + }; + } + + private function extractJsonParameters($arguments) + { + $this->checkArguments($arguments); + + if (count($arguments) == 0) { + return NULL; + } + return $arguments[0]; + } + + private function checkArguments($arguments) + { + if (count($arguments) > 1) { + throw new Exception('You cannot call a command with multiple method arguments.'); + } + } + + /** + * @param string $commandName The called method name + * defined as a key in initCommands() + * @param array $jsonParameters + * @return PHPUnit_Extensions_Selenium2TestCase_Command + */ + protected function newCommand($commandName, $jsonParameters) + { + if (isset($this->commands[$commandName])) { + $factoryMethod = $this->commands[$commandName]; + $url = $this->url->addCommand($commandName); + $command = $factoryMethod($jsonParameters, $url); + return $command; + } + throw new BadMethodCallException("The command '$commandName' is not existent or not supported yet."); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.12 + */ + +/** + * Class to hold the special keys Unicode entities + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.3.0 + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value + */ +class PHPUnit_Extensions_Selenium2TestCase_Keys +{ + const NULL = "\xEE\x80\x80"; + const CANCEL = "\xEE\x80\x81"; + const HELP = "\xEE\x80\x82"; + const BACKSPACE = "\xEE\x80\x83"; + const TAB = "\xEE\x80\x84"; + const CLEAR = "\xEE\x80\x85"; + const RETURN_ = "\xEE\x80\x86"; + const ENTER = "\xEE\x80\x87"; + const SHIFT = "\xEE\x80\x88"; + const CONTROL = "\xEE\x80\x89"; + const ALT = "\xEE\x80\x8A"; + const PAUSE = "\xEE\x80\x8B"; + const ESCAPE = "\xEE\x80\x8C"; + const SPACE = "\xEE\x80\x8D"; + const PAGEUP = "\xEE\x80\x8E"; + const PAGEDOWN = "\xEE\x80\x8F"; + const END = "\xEE\x80\x90"; + const HOME = "\xEE\x80\x91"; + const LEFT = "\xEE\x80\x92"; + const UP = "\xEE\x80\x93"; + const RIGHT = "\xEE\x80\x94"; + const DOWN = "\xEE\x80\x95"; + const INSERT = "\xEE\x80\x96"; + const DELETE = "\xEE\x80\x97"; + const SEMICOLON = "\xEE\x80\x98"; + const EQUALS = "\xEE\x80\x99"; + const NUMPAD0 = "\xEE\x80\x9A"; + const NUMPAD1 = "\xEE\x80\x9B"; + const NUMPAD2 = "\xEE\x80\x9C"; + const NUMPAD3 = "\xEE\x80\x9D"; + const NUMPAD4 = "\xEE\x80\x9E"; + const NUMPAD5 = "\xEE\x80\x9F"; + const NUMPAD6 = "\xEE\x80\xA0"; + const NUMPAD7 = "\xEE\x80\xA1"; + const NUMPAD8 = "\xEE\x80\xA2"; + const NUMPAD9 = "\xEE\x80\xA3"; + const MULTIPLY = "\xEE\x80\xA4"; + const ADD = "\xEE\x80\xA5"; + const SEPARATOR = "\xEE\x80\xA6"; + const SUBTRACT = "\xEE\x80\xA7"; + const DECIMAL = "\xEE\x80\xA8"; + const DIVIDE = "\xEE\x80\xA9"; + const F1 = "\xEE\x80\xB1"; + const F2 = "\xEE\x80\xB2"; + const F3 = "\xEE\x80\xB3"; + const F4 = "\xEE\x80\xB4"; + const F5 = "\xEE\x80\xB5"; + const F6 = "\xEE\x80\xB6"; + const F7 = "\xEE\x80\xB7"; + const F8 = "\xEE\x80\xB8"; + const F9 = "\xEE\x80\xB9"; + const F10 = "\xEE\x80\xBA"; + const F11 = "\xEE\x80\xBB"; + const F12 = "\xEE\x80\xBC"; + const COMMAND = "\xEE\x80\xBD"; +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Object representing an HTTP response from the Selenium Server. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_Response +{ + /** + * @var array decoded response + */ + private $jsonResponse; + + /** + * @var array CURL info for the response. + */ + private $info; + + public function __construct($jsonResponse, $info) + { + $this->jsonResponse = $jsonResponse; + $this->info = $info; + } + + public function getValue() + { + if (isset($this->jsonResponse['value'])) { + return $this->jsonResponse['value']; + } + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function getURL() + { + $url = $this->info['url']; + $sessionId = $this->jsonResponse['sessionId']; + + // if url doesn't have sessionId included - append it manually + // this change was performed in selenium v2.34 + // @see https://code.google.com/p/selenium/issues/detail?id=6089 + // @see https://github.com/sebastianbergmann/phpunit-selenium/issues/265 + if (strpos($url, $sessionId) === FALSE) { + $url .= '/' . $sessionId; + } + + return new PHPUnit_Extensions_Selenium2TestCase_URL($url); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.5 + */ + +/** + * Object representing a browser window. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.5 + * @method array size(array $size = null) Window size as array('width' => $x, 'height' => $y) + * @method array position(array $position = null) Window position as array('x' => $x, 'y' => $y) + * @method array maximize() Maximize window + */ +class PHPUnit_Extensions_Selenium2TestCase_Window extends PHPUnit_Extensions_Selenium2TestCase_CommandsHolder +{ + /** + * @return array class names + */ + protected function initCommands() + { + return array( + 'size' => 'PHPUnit_Extensions_Selenium2TestCase_StateCommand', + 'position' => 'PHPUnit_Extensions_Selenium2TestCase_StateCommand', + 'maximize' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + ); + } + +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Retrieves the value of a CSS property. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Css + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + /** + * @param array $propertyName + */ + public function __construct($propertyName, + PHPUnit_Extensions_Selenium2TestCase_URL $cssResourceBaseUrl) + { + $this->jsonParameters = array(); + $this->url = $cssResourceBaseUrl->descend($propertyName); + } + + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Class for implementing commands that just accomplishes an action (via POST). + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Class for implementing commands that just return a value + * (obtained with GET). + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Get and set the element's value attribute. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Value + extends PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Keys +{ + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + throw new BadMethodCallException("JSON Wire Protocol only supports POST to /value now. To get the value of an element GET /attribute/:naem should be used and this object should never be involved."); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Checks equality (same element on the page) with another DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Equals + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + /** + * @param array $parameter + */ + public function __construct($parameter, + PHPUnit_Extensions_Selenium2TestCase_URL $equalsResourceBaseUrl) + { + $this->jsonParameters = array(); + if (!($parameter instanceof PHPUnit_Extensions_Selenium2TestCase_Element)) { + throw new InvalidArgumentException("Elements can only test equality with other Element instances."); + } + $this->url = $equalsResourceBaseUrl->descend($parameter->getId()); + } + + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Retrieves an attribute of a DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Attribute + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + /** + * @param array $parameter + */ + public function __construct($parameter, + PHPUnit_Extensions_Selenium2TestCase_URL $attributeResourceBaseUrl) + { + $this->jsonParameters = array(); + $this->url = $attributeResourceBaseUrl->descend($parameter); + } + + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Clicks ok on an alert popup. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Click + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.8 + */ + +/** + * Base class for implementing commands with special semantics. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.8 + */ +class PHPUnit_Extensions_Selenium2TestCase_ScreenshotListener implements PHPUnit_Framework_TestListener +{ + private $directory; + + public function __construct($directory) + { + $this->directory = $directory; + } + + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->storeAScreenshot($test); + } + + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->storeAScreenshot($test); + } + + private function storeAScreenshot(PHPUnit_Framework_Test $test) + { + if ($test instanceof PHPUnit_Extensions_Selenium2TestCase) + { + try { + $file = $this->directory . '/' . get_class($test) . '__' . $test->getName() . '__' . date('Y-m-d\TH-i-s') . '.png'; + file_put_contents($file, $test->currentScreenshot()); + } catch (Exception $e) { + $file = $this->directory . '/' . get_class($test) . '__' . $test->getName() . '__' . date('Y-m-d\TH-i-s') . '.txt'; + file_put_contents($file, "Screenshot generation doesn't work." . "\n" + . $e->getMessage() . "\n" + . $e->getTraceAsString()); + } + } + } + + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {} + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {} + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) {} + public function startTest(PHPUnit_Framework_Test $test) {} + public function endTest(PHPUnit_Framework_Test $test, $time) {} + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {} + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {} +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Base class for implementing commands with special semantics. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +abstract class PHPUnit_Extensions_Selenium2TestCase_Command +{ + protected $jsonParameters; + private $commandName; + + /** + * @param array $jsonParameters null in case of no parameters + */ + public function __construct($jsonParameters, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (!is_array($jsonParameters) && $jsonParameters !== NULL) { + throw new InvalidArgumentException("The JSON parameters must be an array, or a NULL value in case they are not required."); + } + $this->jsonParameters = $jsonParameters; + $this->url = $url; + } + + public function url() + { + return $this->url; + } + + /** + * @return string + */ + abstract public function httpMethod(); + + /** + * @param array $jsonParameters null in case of no parameters + */ + public function jsonParameters() + { + return $this->jsonParameters; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Andrew Krasichkov + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.3.2 + */ + +/** + * Get the log for a given log type. Log buffer is reset after each request. + * + * @package PHPUnit_Selenium + * @author Andrew Krasichkov + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.3.2 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Log + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($type, $commandUrl) + { + $jsonParameters = array('type' => $type); + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + return 'POST'; + } +}. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Christian Soronellas + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or sets the current URL of the window. + * + * @package PHPUnit_Selenium + * @author Christian Soronellas + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Keys + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($jsonParameters, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if ($jsonParameters === NULL) { + parent::__construct(NULL, $url); + } else { + $jsonParameters = $this->keysForText($jsonParameters); + parent::__construct($jsonParameters, $url); + } + } + + /** + * @return string + */ + public function httpMethod() + { + return 'POST'; + } + + /** + * Given a string returns an array of the characters that compose the string + * + * @param string $text + * @throws InvalidArgumentException + * @return array + */ + public function keysForText($text) + { + if (is_scalar($text)) { + return array('value' => preg_split('//u', (string) $text, -1, PREG_SPLIT_NO_EMPTY)); + } + if (is_array($text)) { + return $text; + } + throw new InvalidArgumentException('The "text" argument should be a string or an array of special characters!'); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Changes the focus to a window. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Window + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($name, $commandUrl) + { + $jsonParameters = array('name' => $name); + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or posts an attribute from/to the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.9 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Location + extends PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAttribute +{ + + public function __construct($location, $commandUrl) + { + if ($location !== NULL) { + $jsonParameters = array('location' => $location); + } else { + $jsonParameters = NULL; + } + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets an attribute from the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAccessor + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release + */ + +/** + * Gets the active element from the session + * + * @package PHPUnit_Selenium + * @author Marcel Erz + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Active + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + + public function __construct($jsonParameters, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $url = $url->addCommand('element')->addCommand('active'); + parent::__construct($jsonParameters, $url); + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or sets the current URL of the window. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Url + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($url, $commandUrl, PHPUnit_Extensions_Selenium2TestCase_URL $baseUrl) + { + if ($url !== NULL) { + $absoluteLocation = $baseUrl->jump($url)->getValue(); + $jsonParameters = array('url' => $absoluteLocation); + } else { + $jsonParameters = NULL; + } + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Clicks Ok on an alert popup. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_AcceptAlert + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or posts an attribute from/to the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.9 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Orientation + extends PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAttribute +{ + + public function __construct($orientation, $commandUrl) + { + if ($orientation !== NULL) { + $jsonParameters = array('orientation' => $orientation); + } else { + $jsonParameters = NULL; + } + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or posts an attribute from/to the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.9 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAttribute + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Changes the focus to a frame. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Frame + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($id, $commandUrl) + { + $jsonParameters = array( + 'id' => $this->extractId($id) + ); + + parent::__construct($jsonParameters, $commandUrl); + } + + /** + * @param $id + * @return array + */ + private function extractId($id) + { + if ($this->isElement($id)) { //selenium-element + return $id->toWebDriverObject(); + } + + //html-id or null + return $id; + } + + /** + * @param $id + * @return bool + */ + private function isElement($id) + { + return $id instanceof PHPUnit_Extensions_Selenium2TestCase_Element; + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Clicks Cancel on an alert popup. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_DismissAlert + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.3.2 + */ + +/** + * Sends a file to a RC + * Returns the FQ path to the transfered file + * + * @package PHPUnit_Selenium + * @author Kevin Ran + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.3.2 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_File + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + + /** + * @var + */ + private static $_zipArchive; + + public function __construct($argument, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (!is_file($argument)) { + throw new BadMethodCallException("No such file: {$argument}"); + } + + $zipfile_path = $this->_zipArchiveFile($argument); + $contents = file_get_contents($zipfile_path); + + if ($contents === false) { + throw new Exception("Unable to read generated zip file: {$zipfile_path}"); + } + + $file = base64_encode($contents); + + parent::__construct(array('file' => $file), $url); + + unlink($zipfile_path); + } + + public function httpMethod() + { + return 'POST'; + } + + /** + * Creates a zip archive with the given file + * + * @param string $file_path FQ path to file + * @return string Generated zip file + */ + protected function _zipArchiveFile( $file_path ) + { + + // file MUST be readable + if( !is_readable( $file_path ) ) { + + throw new Exception( "Unable to read {$file_path}" ); + + } // if !file_data + + $filename_hash = sha1( time() . $file_path ); + $tmp_dir = $this->_getTmpDir(); + $zip_filename = "{$tmp_dir}{$filename_hash}.zip"; + $zip = $this->_getZipArchiver(); + + if ($zip->open($zip_filename, ZIPARCHIVE::CREATE) === FALSE) { + throw new Exception( "Unable to create zip archive: {$zip_filename}" ); + } + + $zip->addFile($file_path, basename($file_path)); + $zip->close(); + + return $zip_filename; + } + + /** + * Returns a runtime instance of a ZipArchive + * + * @return ZipArchive + */ + protected function _getZipArchiver() + { + // create ZipArchive if necessary + if (!static::$_zipArchive) { + static::$_zipArchive = new ZipArchive(); + } + + return static::$_zipArchive; + } + + /** + * Calls sys_get_temp_dir and ensures that it has a trailing slash + * ( behavior varies across systems ) + * + * @return string + */ + protected function _getTmpDir() + { + return rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Obtains the text of an alert, or types into a prompt. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_AlertText + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($argument, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (is_string($argument)) { + $jsonParameters =array('text' => $argument); + } else if ($argument == NULL) { + $jsonParameters = NULL; + } else { + throw new BadMethodCallException('Wrong parameters for alertText().'); + } + parent::__construct($jsonParameters, $url); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.13 + */ + +/** + * Sends session click command for emulating LEFT, MIDDLE or RIGHT mouse buttons + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.13 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Click + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + const LEFT = 0; + const MIDDLE = 1; + const RIGHT = 2; + + public function __construct($argument, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (is_null($argument)) { + $jsonParameters = NULL; + } elseif (!is_scalar($argument) || !in_array($argument, array( + self::LEFT, self::RIGHT, self::MIDDLE + ))) { + throw new BadMethodCallException('Wrong parameter for click(): expecting 0, 1 or 2.'); + } else { + $jsonParameters = array('button' => $argument); + } + + parent::__construct($jsonParameters, $url); + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.8 + */ + +/** + * Moves the mouse pointer. + * + * @author Giorgio Sironi + * @package PHPUnit_Selenium + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.8 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_MoveTo + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($element, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (!is_array($element)) { + $element = array( + 'element' => $element, + ); + } + + $validKeys = array( + 'element' => NULL, + 'xoffset' => NULL, + 'yoffset' => NULL, + ); + + $jsonParameters = array_intersect_key($element, $validKeys); + + if (isset($jsonParameters['element'])) { + if (!($jsonParameters['element'] instanceof PHPUnit_Extensions_Selenium2TestCase_Element)) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception('Only moving over an element is supported. Please pass a PHPUnit_Extensions_Selenium2TestCase_Element instance.'); + } + + $jsonParameters['element'] = $jsonParameters['element']->getId(); + } + + if (isset($jsonParameters['xoffset']) || isset($jsonParameters['yoffset'])) { + // @see https://github.com/sebastianbergmann/phpunit-selenium/pull/250#issuecomment-21308153 + // @see https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/moveto + error_log('Even though this method is a part of the WebDriver Wire protocol it might be not supported by your browser yet'); + } + + parent::__construct($jsonParameters, $url); + } + + /** + * @return string + */ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.9 + */ + +/** + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.8 + */ +class PHPUnit_Extensions_Selenium2TestCase_NoSeleniumException + extends PHPUnit_Extensions_Selenium2TestCase_Exception +{ +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Browser session for Selenium 2: main point of entry for functionality. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + * @method void acceptAlert() Press OK on an alert, or confirms a dialog + * @method mixed alertText($value = NULL) Gets the alert dialog text, or sets the text for a prompt dialog + * @method void back() + * @method void dismissAlert() Press Cancel on an alert, or does not confirm a dialog + * @method void doubleclick() Double-clicks at the current mouse coordinates (set by moveto). + * @method string execute(array $javaScriptCode) Injects arbitrary JavaScript in the page and returns the last. See unit tests for usage + * @method string executeAsync(array $javaScriptCode) Injects arbitrary JavaScript and wait for the callback (last element of arguments) to be called. See unit tests for usage + * @method void forward() + * @method void frame(mixed $element) Changes the focus to a frame in the page (by frameCount of type int, htmlId of type string, htmlName of type string or element of type \PHPUnit_Extensions_Selenium2TestCase_Element) + * @method void moveto(\PHPUnit_Extensions_Selenium2TestCase_Element $element) Move the mouse by an offset of the specificed element. + * @method void refresh() + * @method string source() Returns the HTML source of the page + * @method string title() + * @method void|string url($url = NULL) + * @method void window($name) Changes the focus to another window + * @method string windowHandle() Retrieves the current window handle + * @method string windowHandles() Retrieves a list of all available window handles + * @method string keys() Send a sequence of key strokes to the active element. + * @method string file($file_path) Upload a local file. Returns the fully qualified path to the transferred file. + * @method array log(string $type) Get the log for a given log type. Log buffer is reset after each request. + * @method array logTypes() Get available log types. + */ +class PHPUnit_Extensions_Selenium2TestCase_Session + extends PHPUnit_Extensions_Selenium2TestCase_Element_Accessor +{ + /** + * @var string the base URL for this session, + * which all relative URLs will refer to + */ + private $baseUrl; + + /** + * @var PHPUnit_Extensions_Selenium2TestCase_Session_Timeouts + */ + private $timeouts; + + /** + * @var boolean + */ + private $stopped = FALSE; + + public function __construct($driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url, + PHPUnit_Extensions_Selenium2TestCase_URL $baseUrl, + PHPUnit_Extensions_Selenium2TestCase_Session_Timeouts $timeouts) + { + $this->baseUrl = $baseUrl; + $this->timeouts = $timeouts; + parent::__construct($driver, $url); + } + + /** + * @return string + */ + public function id() + { + return $this->url->lastSegment(); + } + + protected function initCommands() + { + $baseUrl = $this->baseUrl; + return array( + 'acceptAlert' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_AcceptAlert', + 'alertText' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_AlertText', + 'back' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'click' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Click', + 'buttondown' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'buttonup' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'dismissAlert' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_DismissAlert', + 'doubleclick' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'execute' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'executeAsync' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'forward' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'frame' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Frame', + 'keys' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Keys', + 'moveto' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_MoveTo', + 'refresh' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'screenshot' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'source' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAccessor', + 'title' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAccessor', + 'log' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Log', + 'logTypes' => $this->attributeCommandFactoryMethod('log/types'), + 'url' => function ($jsonParameters, $commandUrl) use ($baseUrl) { + return new PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Url($jsonParameters, $commandUrl, $baseUrl); + }, + 'window' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Window', + 'windowHandle' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAccessor', + 'windowHandles' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAccessor', + 'touchDown' => $this->touchCommandFactoryMethod('touch/down'), + 'touchUp' => $this->touchCommandFactoryMethod('touch/up'), + 'touchMove' => $this->touchCommandFactoryMethod('touch/move'), + 'touchScroll' => $this->touchCommandFactoryMethod('touch/scroll'), + 'flick' => $this->touchCommandFactoryMethod('touch/flick'), + 'location' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Location', + 'orientation' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Orientation', + 'file' => 'PHPUnit_Extensions_Selenium2TestCase_SessionCommand_File' + ); + } + + private function attributeCommandFactoryMethod($urlSegment) + { + $url = $this->url->addCommand($urlSegment); + return function ($jsonParameters, $commandUrl) use ($url) { + return new PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAttribute($jsonParameters, $url); + }; + } + + private function touchCommandFactoryMethod($urlSegment) + { + $url = $this->url->addCommand($urlSegment); + return function ($jsonParameters, $commandUrl) use ($url) { + return new PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost($jsonParameters, $url); + }; + } + + public function __destruct() + { + $this->stop(); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function getSessionUrl() + { + return $this->url; + } + + /** + * Closed the browser. + * @return void + */ + public function stop() + { + if ($this->stopped) { + return; + } + try { + $this->driver->curl('DELETE', $this->url); + } catch (Exception $e) { + // sessions which aren't closed because of sharing can time out on the server. In no way trying to close them should make a test fail, as it already finished before arriving here. + "Closing sessions: " . $e->getMessage() . "\n"; + } + $this->stopped = TRUE; + if ($this->stopped) { + return; + } + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_Element_Select + */ + public function select(PHPUnit_Extensions_Selenium2TestCase_Element $element) + { + $tag = $element->name(); + if ($tag !== 'select') { + throw new InvalidArgumentException("The element is not a `select` tag but a `$tag`."); + } + return PHPUnit_Extensions_Selenium2TestCase_Element_Select::fromElement($element); + } + + /** + * @param array WebElement JSON object + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function elementFromResponseValue($value) + { + return PHPUnit_Extensions_Selenium2TestCase_Element::fromResponseValue($value, $this->getSessionUrl()->descend('element'), $this->driver); + } + + /** + * @param string $id id attribute, e.g. 'container' + * @return void + */ + public function clickOnElement($id) + { + return $this->element($this->using('id')->value($id))->click(); + } + + public function timeouts() + { + return $this->timeouts; + } + + /** + * @return string a BLOB of a PNG file + */ + public function currentScreenshot() + { + return base64_decode($this->screenshot()); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_Window + */ + public function currentWindow() + { + $url = $this->url->descend('window')->descend(trim($this->windowHandle(), '{}')); + return new PHPUnit_Extensions_Selenium2TestCase_Window($this->driver, $url); + } + + public function closeWindow() + { + $this->driver->curl('DELETE', $this->url->descend('window')); + } + + /** + * Get the element on the page that currently has focus. + * + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function active() + { + $command = new PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Active(null, $this->url); + $response = $this->driver->execute($command); + return $this->elementFromResponseValue($response->getValue()); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie + */ + public function cookie() + { + $url = $this->url->descend('cookie'); + return new PHPUnit_Extensions_Selenium2TestCase_Session_Cookie($this->driver, $url); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Storage + */ + public function localStorage() + { + $url = $this->url->addCommand('localStorage'); + return new PHPUnit_Extensions_Selenium2TestCase_Session_Storage($this->driver, $url); + } + + public function landscape() + { + $this->orientation('LANDSCAPE'); + } + + public function portrait() + { + $this->orientation('PORTRAIT'); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.12 + */ + +/** + * The WaitUntil implementation, inspired by Java and .NET clients + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.12 + * @see http://selenium.googlecode.com/svn/trunk/dotnet/src/WebDriver.Support/UI/WebDriverWait.cs + * @see http://selenium.googlecode.com/svn/trunk/java/client/src/org/openqa/selenium/support/ui/FluentWait.java + */ +class PHPUnit_Extensions_Selenium2TestCase_WaitUntil +{ + /** + * PHPUnit Test Case instance + * + * @var PHPUnit_Extensions_Selenium2TestCase + */ + private $_testCase; + + /** + * Default timeout, ms + * + * @var int + */ + private $_defaultTimeout = 0; + + /** + * The sleep interval between iterations, ms + * + * @var int + */ + private $_defaultSleepInterval = 500; + + /** + * @param PHPUnit_Extensions_Selenium2TestCase $testCase + */ + public function __construct(PHPUnit_Extensions_Selenium2TestCase $testCase) + { + $this->_testCase = $testCase; + } + + /** + * @param $callback Callback to run until it returns not null or timeout occurs + * @param null $timeout + * @return mixed + * @throws PHPUnit_Extensions_Selenium2TestCase_Exception + * @throws PHPUnit_Extensions_Selenium2TestCase_WebDriverException + */ + public function run($callback, $timeout = NULL) + { + if (!is_callable($callback)) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception('The valid callback is expected'); + } + + // if there was an implicit timeout specified - remember it and temporarily turn it off + $implicitWait = $this->_testCase->timeouts()->getLastImplicitWaitValue(); + if ($implicitWait) { + $this->_testCase->timeouts()->implicitWait(0); + } + + if (is_null($timeout)) { + $timeout = $this->_defaultTimeout; + } + + $timeout /= 1000; + + $endTime = microtime(TRUE) + $timeout; + + $lastException = NULL; + + while (TRUE) { + try { + $result = call_user_func($callback, $this->_testCase); + + if (!is_null($result)) { + if ($implicitWait) { + $this->_testCase->timeouts()->implicitWait($implicitWait); + } + + return $result; + } + } catch(Exception $e) { + $lastException = $e; + } + + if (microtime(TRUE) > $endTime) { + if ($implicitWait) { + $this->_testCase->timeouts()->implicitWait($implicitWait); + } + + $message = "Timed out after {$timeout} second" . ($timeout != 1 ? 's' : ''); + throw new PHPUnit_Extensions_Selenium2TestCase_WebDriverException($message, + PHPUnit_Extensions_Selenium2TestCase_WebDriverException::Timeout, $lastException); + } + + usleep($this->_defaultSleepInterval * 1000); + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Keeps a Session object shared between test runs to save time. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Shared + implements PHPUnit_Extensions_Selenium2TestCase_SessionStrategy +{ + private $original; + private $session; + private $mainWindow; + private $lastTestWasNotSuccessful = FALSE; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_SessionStrategy $originalStrategy) + { + $this->original = $originalStrategy; + } + + public function session(array $parameters) + { + if ($this->lastTestWasNotSuccessful) { + if ($this->session !== NULL) { + $this->session->stop(); + $this->session = NULL; + } + $this->lastTestWasNotSuccessful = FALSE; + } + if ($this->session === NULL) { + $this->session = $this->original->session($parameters); + $this->mainWindow = $this->session->windowHandle(); + } else { + $this->session->window($this->mainWindow); + } + return $this->session; + } + + public function notSuccessfulTest() + { + $this->lastTestWasNotSuccessful = TRUE; + } + + public function endOfTest(PHPUnit_Extensions_Selenium2TestCase_Session $session = NULL) + { + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Produces a new Session object shared for each test. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Isolated + implements PHPUnit_Extensions_Selenium2TestCase_SessionStrategy +{ + public function session(array $parameters) + { + $seleniumServerUrl = PHPUnit_Extensions_Selenium2TestCase_URL::fromHostAndPort($parameters['host'], $parameters['port']); + $driver = new PHPUnit_Extensions_Selenium2TestCase_Driver($seleniumServerUrl, $parameters['seleniumServerRequestsTimeout']); + $capabilities = array_merge($parameters['desiredCapabilities'], + array( + 'browserName' => $parameters['browserName'] + )); + $session = $driver->startSession($capabilities, $parameters['browserUrl']); + return $session; + } + + public function notSuccessfulTest() + { + } + + public function endOfTest(PHPUnit_Extensions_Selenium2TestCase_Session $session = NULL) + { + if ($session !== NULL) { + $session->stop(); + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Object representing a DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + * @method string attribute($name) Retrieves an element's attribute + * @method void clear() Empties the content of a form element. + * @method void click() Clicks on element + * @method string css($propertyName) Retrieves the value of a CSS property + * @method bool displayed() Checks an element's visibility + * @method bool enabled() Checks a form element's state + * @method bool equals(PHPUnit_Extensions_Selenium2TestCase_Element $another) Checks if the two elements are the same on the page + * @method array location() Retrieves the element's position in the page: keys 'x' and 'y' in the returned array + * @method bool selected() Checks the state of an option or other form element + * @method array size() Retrieves the dimensions of the element: 'width' and 'height' of the returned array + * @method void submit() Submits a form; can be called on its children + * @method string text() Get content of ordinary elements + */ +class PHPUnit_Extensions_Selenium2TestCase_Element + extends PHPUnit_Extensions_Selenium2TestCase_Element_Accessor +{ + /** + * @return \self + * @throws InvalidArgumentException + */ + public static function fromResponseValue( + array $value, + PHPUnit_Extensions_Selenium2TestCase_URL $parentFolder, + PHPUnit_Extensions_Selenium2TestCase_Driver $driver) + { + if (!isset($value['ELEMENT'])) { + throw new InvalidArgumentException('Element not found.'); + } + $url = $parentFolder->descend($value['ELEMENT']); + return new self($driver, $url); + } + + /** + * @return integer + */ + public function getId() + { + return $this->url->lastSegment(); + } + + /** + * @return array class names + */ + protected function initCommands() + { + return array( + 'attribute' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Attribute', + 'clear' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'click' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Click', + 'css' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Css', + 'displayed' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'enabled' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'equals' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Equals', + 'location' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'name' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'selected' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'size' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'submit' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'text' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'value' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Value', + 'tap' => $this->touchCommandFactoryMethod('touch/click'), + 'scroll' => $this->touchCommandFactoryMethod('touch/scroll'), + 'doubletap' => $this->touchCommandFactoryMethod('touch/doubleclick'), + 'longtap' => $this->touchCommandFactoryMethod('touch/longclick'), + 'flick' => $this->touchCommandFactoryMethod('touch/flick') + ); + } + + protected function getSessionUrl() + { + return $this->url->ascend()->ascend(); + } + + private function touchCommandFactoryMethod($urlSegment) + { + $url = $this->getSessionUrl()->addCommand($urlSegment); + $self = $this; + return function ($jsonParameters, $commandUrl) use ($url, $self) { + if ((is_array($jsonParameters) && + !isset($jsonParameters['element'])) || + is_null($jsonParameters)) { + $jsonParameters['element'] = $self->getId(); + } + return new PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost($jsonParameters, $url); + }; + } + + /** + * Retrieves the tag name + * @return string + */ + public function name() + { + return strtolower(parent::name()); + } + + /** + * Generates an array that is structured as the WebDriver Object of the JSONWireProtocoll + * + * @return array + */ + public function toWebDriverObject() + { + return array('ELEMENT' => (string)$this->getId()); + } + + /** + * Get or set value of form elements. If the element already has a value, the set one will be appended to it. + * Created **ONLY** for keeping backward compatibility, since in selenium v2.42.0 it was removed + * The currently recommended solution is to use `$element->attribute('value')` + * @see https://code.google.com/p/selenium/source/detail?r=953007b48e83f90450f3e41b11ec31e2928f1605 + * @see https://code.google.com/p/selenium/source/browse/java/CHANGELOG + * + * @param string $newValue + * @return null|string + */ + public function value($newValue = NULL) + { + if ($newValue !== NULL) { + return parent::value($newValue); + } + + return $this->attribute('value'); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Specifies how to create Session objects for running tests. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +interface PHPUnit_Extensions_Selenium2TestCase_SessionStrategy +{ + /** + * @param array $parameters 'host' => Selenium Server machine + 'port' => Selenium Server port + 'browser' => a browser name + * 'browserUrl' => base URL to use during the test + */ + public function session(array $parameters); + + public function notSuccessfulTest(); + + public function endOfTest(PHPUnit_Extensions_Selenium2TestCase_Session $session = NULL); +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Driver for creating browser session with Selenium 2 (WebDriver API). + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_Driver +{ + private $seleniumServerUrl; + private $seleniumServerRequestsTimeout; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_URL $seleniumServerUrl, $timeout = 60) + { + $this->seleniumServerUrl = $seleniumServerUrl; + $this->seleniumServerRequestsTimeout = $timeout; + } + + public function startSession(array $desiredCapabilities, PHPUnit_Extensions_Selenium2TestCase_URL $browserUrl) + { + $sessionCreation = $this->seleniumServerUrl->descend("/wd/hub/session"); + $response = $this->curl('POST', $sessionCreation, array( + 'desiredCapabilities' => $desiredCapabilities + )); + $sessionPrefix = $response->getURL(); + + $timeouts = new PHPUnit_Extensions_Selenium2TestCase_Session_Timeouts( + $this, + $sessionPrefix->descend('timeouts'), + $this->seleniumServerRequestsTimeout * 1000 + ); + return new PHPUnit_Extensions_Selenium2TestCase_Session( + $this, + $sessionPrefix, + $browserUrl, + $timeouts + ); + } + + /** + * Performs an HTTP request to the Selenium 2 server. + * + * @param string $method 'GET'|'POST'|'DELETE'|... + * @param string $url + * @param array $params JSON parameters for POST requests + */ + public function curl($http_method, + PHPUnit_Extensions_Selenium2TestCase_URL $url, + $params = NULL) + { + $curl = curl_init($url->getValue()); + curl_setopt($curl, CURLOPT_TIMEOUT, $this->seleniumServerRequestsTimeout); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($curl, + CURLOPT_HTTPHEADER, + array( + 'Content-type: application/json;charset=UTF-8', + 'Accept: application/json;charset=UTF-8' + )); + + if ($http_method === 'POST') { + curl_setopt($curl, CURLOPT_POST, TRUE); + if ($params && is_array($params)) { + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params)); + } else { + curl_setopt($curl, CURLOPT_POSTFIELDS, ''); + } + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE); + } else if ($http_method == 'DELETE') { + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + } + + $rawResponse = trim(curl_exec($curl)); + if (curl_errno($curl)) { + throw new PHPUnit_Extensions_Selenium2TestCase_NoSeleniumException( + 'Error connection[' . curl_errno($curl) . '] to ' . + $url->getValue() . ': ' . curl_error($curl) + ); + } + $info = curl_getinfo($curl); + if ($info['http_code'] == 0) { + throw new PHPUnit_Extensions_Selenium2TestCase_NoSeleniumException(); + } + if ($info['http_code'] == 404) { + throw new BadMethodCallException("The command $url is not recognized by the server."); + } + curl_close($curl); + $content = json_decode($rawResponse, TRUE); + if ($info['http_code'] == 500) { + $message = ''; + if (isset($content['value']['message'])) { + $message .= $content['value']['message']; + } else { + $message .= "Internal server error while executing $http_method request at $url. Response: " . var_export($content, TRUE); + } + if (isset($content['value']['class'])) { + $message .= PHP_EOL . $content['value']['class']; + } + throw new PHPUnit_Extensions_Selenium2TestCase_WebDriverException($message, isset($content['status']) ? $content['status'] : 13); + } + return new PHPUnit_Extensions_Selenium2TestCase_Response($content, $info); + } + + public function execute(PHPUnit_Extensions_Selenium2TestCase_Command $command) + { + return $this->curl($command->httpMethod(), + $command->url(), + $command->jsonParameters()); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * URL Value Object allowing easy concatenation. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +final class PHPUnit_Extensions_Selenium2TestCase_URL +{ + /** + * @var string + */ + private $value; + + /** + * @param string $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * @param string $host + * @param int port + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public static function fromHostAndPort($host, $port) + { + return new self("http://{$host}:{$port}"); + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return $this->getValue(); + } + + /** + * @param string $addition + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function descend($addition) + { + if ($addition == '') { + // if we're adding nothing, respect the current url's choice of + // whether or not to include a trailing slash; prevents inadvertent + // adding of slashes to urls that can't handle it + $newValue = $this->value; + } else { + $newValue = rtrim($this->value, '/') + . '/' + . ltrim($addition, '/'); + } + return new self($newValue); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function ascend() + { + $lastSlash = strrpos($this->value, "/"); + $newValue = substr($this->value, 0, $lastSlash); + return new self($newValue); + } + + /** + * @return string + */ + public function lastSegment() + { + $segments = explode('/', $this->value); + return end($segments); + } + + /** + * @param string $command + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function addCommand($command) + { + return $this->descend($this->camelCaseToUnderScores($command)); + } + + /** + * @param string $newUrl + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function jump($newUrl) + { + if ($this->isAbsolute($newUrl)) { + return new self($newUrl); + } else { + return $this->descend($newUrl); + } + } + + private function camelCaseToUnderScores($string) + { + $string = preg_replace('/([A-Z]{1,1})/', ' \1', $string); + $string = strtolower($string); + return str_replace(' ', '_', $string); + } + + private function isAbsolute($urlValue) + { + return preg_match('/^(http|https):\/\//', $urlValue) > 0; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Christian Becker + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since + */ + +/** + * Indicates an exception as a result of a non-sucessful WebDriver response status code. + * + * @package PHPUnit_Selenium + * @author Christian Becker + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since + */ +class PHPUnit_Extensions_Selenium2TestCase_WebDriverException extends PHPUnit_Extensions_Selenium2TestCase_Exception +{ + /* @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes */ + const Success = 0; + const NoSuchDriver = 6; + const NoSuchElement = 7; + const NoSuchFrame = 8; + const UnknownCommand = 9; + const StaleElementReference = 10; + const ElementNotVisible = 11; + const InvalidElementState = 12; + const UnknownError = 13; + const ElementIsNotSelectable = 15; + const JavaScriptError = 17; + const XPathLookupError = 19; + const Timeout = 21; + const NoSuchWindow = 23; + const InvalidCookieDomain = 24; + const UnableToSetCookie = 25; + const UnexpectedAlertOpen = 26; + const NoAlertOpenError = 27; + const ScriptTimeout = 28; + const InvalidElementCoordinates = 29; + const IMENotAvailable = 30; + const IMEEngineActivationFailed = 31; + const InvalidSelector = 32; + const SessionNotCreatedException = 33; + const MoveTargetOutOfBounds = 34; +}. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Manages timeouts for the current browser session. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + * @method implicitWait(int $ms) Sets timeout when searching for elements + * @method asyncScript(int $ms) Sets timeout for asynchronous scripts executed by Session::executeAsync() + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Timeouts + extends PHPUnit_Extensions_Selenium2TestCase_CommandsHolder +{ + private $maximumTimeout; + private $lastImplicitWaitValue = 0; + + public function __construct($driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url, + $maximumTimeout) + { + parent::__construct($driver, $url); + $this->maximumTimeout = $maximumTimeout; + } + + protected function initCommands() + { + $self = $this; + return array( + 'implicitWait' => function ($milliseconds, $commandUrl) use ($self) { + $self->check($milliseconds); + $self->setLastImplicitWaitValue($milliseconds); + $jsonParameters = array('ms' => $milliseconds); + return new PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost($jsonParameters, $commandUrl); + }, + 'asyncScript' => function ($milliseconds, $commandUrl) use ($self) { + $self->check($milliseconds); + $jsonParameters = array('ms' => $milliseconds); + return new PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost($jsonParameters, $commandUrl); + }, + + ); + } + + public function setLastImplicitWaitValue($implicitWait) + { + $this->lastImplicitWaitValue = $implicitWait; + } + + public function getLastImplicitWaitValue() + { + return $this->lastImplicitWaitValue; + } + + public function check($timeout) + { + if ($timeout > $this->maximumTimeout) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception('There is no use in setting this timeout unless you also call $this->setSeleniumServerRequestsTimeout($seconds) in setUp().'); + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Adds and remove cookies. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Cookie +{ + private $driver; + private $url; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_Driver $driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $this->driver = $driver; + $this->url = $url; + } + + /** + * @param string $name + * @param string $value + * @return void + */ + public function add($name, $value) + { + return new PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder($this, $name, $value); + } + + /** + * @param string $name + * @return string + */ + public function get($name) + { + $cookies = $this->driver->curl('GET', $this->url)->getValue(); + foreach ($cookies as $cookie) { + if ($cookie['name'] == $name) { + return $cookie['value']; + } + } + throw new PHPUnit_Extensions_Selenium2TestCase_Exception("There is no '$name' cookie available on this page."); + } + + /** + * @param string $name + * @return void + */ + public function remove($name) + { + $url = $this->url->descend($name); + $this->driver->curl('DELETE', $url); + } + + /** + * @return void + */ + public function clear() + { + $this->driver->curl('DELETE', $this->url); + } + + /** + * @internal + * @param array $data + * @return void + */ + public function postCookie(array $data) + { + $this->driver->curl('POST', + $this->url, + array( + 'cookie' => $data + )); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Manage the local storage HTML 5 database. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Storage +{ + private $driver; + private $url; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_Driver $driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $this->driver = $driver; + $this->url = $url; + } + + public function __set($name, $value) + { + $this->driver->curl('POST', $this->url, array( + 'key' => $name, + 'value' => (string)$value + )); + } + + public function __get($name) + { + return $this->driver->curl( + 'GET', + $this->url->descend('key')->descend($name) + )->getValue(); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Adds a cookie. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder +{ + private $name; + private $value; + private $path; + private $domain; + private $secure = FALSE; + private $expiry; + + public function __construct($cookieFacade, $name, $value) + { + $this->cookieFacade = $cookieFacade; + $this->name = $name; + $this->value = $value; + } + + /** + * @param string + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function path($path) + { + $this->path = $path; + return $this; + } + + /** + * @param string + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function domain($domain) + { + $this->domain = $domain; + return $this; + } + + /** + * @param boolean + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function secure($secure) + { + $this->secure = $secure; + return $this; + } + + /** + * @param integer + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function expiry($expiry) + { + $this->expiry = $expiry; + return $this; + } + + /** + * @return void + */ + public function set() + { + $cookieData = array( + 'name' => $this->name, + 'value' => $this->value, + 'secure' => $this->secure, + ); + foreach (array('path', 'domain', 'expiry') as $parameter) { + if ($this->$parameter !== NULL) { + $cookieData[$parameter] = $this->$parameter; + } + } + $this->cookieFacade->postCookie($cookieData); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Indicates an exception during the execution of Selenium 2 commands. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Exception extends RuntimeException +{ +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.2 + */ + +/** + * Object representing a
+ + + + + + + + + + + + + +{{items}} + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
+ + +{{lines}} + +
+ + + + + + + + + + + + + Dashboard for {{full_path}} + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + +{{insufficient_coverage_classes}} + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + +{{project_risks_classes}} + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + +{{insufficient_coverage_methods}} + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + +{{project_risks_methods}} + +
MethodCRAP
+
+
+
+ +
+ + + + + + + + + + {{name}} + {{classes_bar}} +
{{classes_tested_percent}}
+
{{classes_number}}
+ {{methods_bar}} +
{{methods_tested_percent}}
+
{{methods_number}}
+ {{crap}} + {{lines_bar}} +
{{lines_executed_percent}}
+
{{lines_number}}
+ + + + {{icon}}{{name}} + {{lines_bar}} +
{{lines_executed_percent}}
+
{{lines_number}}
+ {{methods_bar}} +
{{methods_tested_percent}}
+
{{methods_number}}
+ {{classes_bar}} +
{{classes_tested_percent}}
+
{{classes_number}}
+ + +
+
+ {{percent}}% covered ({{level}}) +
+
+ + {{name}} + {{methods_bar}} +
{{methods_tested_percent}}
+
{{methods_number}}
+ {{crap}} + {{lines_bar}} +
{{lines_executed_percent}}
+
{{lines_number}}
+ + +/** +* @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document);!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function r(n){return null===n?0/0:+n}function u(n){return!isNaN(n)}function i(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function l(){this._=Object.create(null)}function s(n){return(n+="")===pa||n[0]===va?va+n:n}function f(n){return(n+="")[0]===va?n.slice(1):n}function h(n){return s(n)in this._}function g(n){return(n=s(n))in this._&&delete this._[n]}function p(){var n=[];for(var t in this._)n.push(f(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function m(){this._=Object.create(null)}function y(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=da.length;r>e;++e){var u=da[e]+t;if(u in n)return u}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function Z(n){return ya(n,Sa),n}function V(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var l=ka.get(n);return l&&(n=l,c=B),a?t?u:r:t?b:i}function $(n,t){return function(e){var r=ta.event;ta.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ta.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Aa,u="click"+r,i=ta.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ea&&(Ea="onselectstart"in e?!1:x(e.style,"userSelect")),Ea){var o=n(e).style,a=o[Ea];o[Ea]="none"}return function(n){if(i.on(r,null),Ea&&(o[Ea]=a),n){var t=function(){i.on(u,null)};i.on(u,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var u=r.createSVGPoint();if(0>Na){var i=t(n);if(i.scrollX||i.scrollY){r=ta.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Na=!(o.f||o.e),r.remove()}}return Na?(u.x=e.pageX,u.y=e.pageY):(u.x=e.clientX,u.y=e.clientY),u=u.matrixTransform(n.getScreenCTM().inverse()),[u.x,u.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ta.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nt(n){return n>1?0:-1>n?qa:Math.acos(n)}function tt(n){return n>1?Ra:-1>n?-Ra:Math.asin(n)}function et(n){return((n=Math.exp(n))-1/n)/2}function rt(n){return((n=Math.exp(n))+1/n)/2}function ut(n){return((n=Math.exp(2*n))-1)/(n+1)}function it(n){return(n=Math.sin(n/2))*n}function ot(){}function at(n,t,e){return this instanceof at?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof at?new at(n.h,n.s,n.l):bt(""+n,_t,at):new at(n,t,e)}function ct(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,new mt(u(n+120),u(n),u(n-120))}function lt(n,t,e){return this instanceof lt?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof lt?new lt(n.h,n.c,n.l):n instanceof ft?gt(n.l,n.a,n.b):gt((n=wt((n=ta.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new lt(n,t,e)}function st(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new ft(e,Math.cos(n*=Da)*t,Math.sin(n)*t)}function ft(n,t,e){return this instanceof ft?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof ft?new ft(n.l,n.a,n.b):n instanceof lt?st(n.h,n.c,n.l):wt((n=mt(n)).r,n.g,n.b):new ft(n,t,e)}function ht(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=pt(u)*Xa,r=pt(r)*$a,i=pt(i)*Ba,new mt(dt(3.2404542*u-1.5371385*r-.4985314*i),dt(-.969266*u+1.8760108*r+.041556*i),dt(.0556434*u-.2040259*r+1.0572252*i))}function gt(n,t,e){return n>0?new lt(Math.atan2(e,t)*Pa,Math.sqrt(t*t+e*e),n):new lt(0/0,0/0,n)}function pt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function vt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function dt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mt(n,t,e){return this instanceof mt?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mt?new mt(n.r,n.g,n.b):bt(""+n,mt,ct):new mt(n,t,e)}function yt(n){return new mt(n>>16,n>>8&255,255&n)}function Mt(n){return yt(n)+""}function xt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ga.get(n.toLowerCase()))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function _t(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),new at(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=vt((.4124564*n+.3575761*t+.1804375*e)/Xa),u=vt((.2126729*n+.7151522*t+.072175*e)/$a),i=vt((.0193339*n+.119192*t+.9503041*e)/Ba);return ft(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Nt(t,e,n,r)}}function Nt(n,t,e,r){function u(){var n,t=c.status;if(!t&&zt(c)||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return void o.error.call(i,r)}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=ta.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=ta.event;ta.event=n;try{o.progress.call(i,c)}finally{ta.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ra(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},ta.rebind(i,o,"on"),null==r?i:i.get(Ct(r))}function Ct(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function zt(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qt(){var n=Lt(),t=Tt()-n;t>24?(isFinite(t)&&(clearTimeout(tc),tc=setTimeout(qt,t)),nc=0):(nc=1,rc(qt))}function Lt(){var n=Date.now();for(ec=Ka;ec;)n>=ec.t&&(ec.f=ec.c(n-ec.t)),ec=ec.n;return n}function Tt(){for(var n,t=Ka,e=1/0;t;)t.f?t=n?n.n=t.n:Ka=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r&&e?function(n,t){for(var u=n.length,i=[],o=0,a=r[0],c=0;u>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),i.push(n.substring(u-=a,u+a)),!((c+=a+1)>t));)a=r[o=(o+1)%r.length];return i.reverse().join(e)}:y;return function(n){var e=ic.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",c=e[4]||"",l=e[5],s=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1,y=!0;switch(h&&(h=+h.substring(1)),(l||"0"===r&&"="===o)&&(l=r="0",o="="),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":y=!1;case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=oc.get(g)||Ut;var M=l&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>p){var c=ta.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=y?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!l&&f&&(x=i(x,1/0));var S=v.length+x.length+b.length+(M?0:u.length),k=s>S?new Array(S=s-S+1).join(r):"";return M&&(x=i(k+x,k.length?s-b.length:1/0)),u+=v,n=x+b,("<"===o?u+n+k:">"===o?k+u+n:"^"===o?k.substring(0,S>>=1)+u+n+k.substring(S):u+(M?n:k+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ft(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new cc(e-1)),1),e}function i(n,e){return t(n=new cc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{cc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{cc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ht(n);return c.floor=c,c.round=Ht(r),c.ceil=Ht(u),c.offset=Ht(i),c.range=a,n}function Ht(n){return function(t,e){try{cc=jt;var r=new jt;return r._=t,n(r,e)._}finally{cc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++aa;){if(r>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in sc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.slice(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,N.c.toString(),t,r)}function c(n,t,r){return e(n,N.x.toString(),t,r)}function l(n,t,r){return e(n,N.X.toString(),t,r)}function s(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{cc=jt;var t=new cc;return t._=n,r(t)}finally{cc=Date}}var r=t(n);return e.parse=function(n){try{cc=jt;var t=r.parse(n);return t&&t._}finally{cc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var M=ta.map(),x=Yt(v),b=Zt(v),_=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),A=Zt(y);p.forEach(function(n,t){M.set(n.toLowerCase(),t)});var N={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+ac.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(ac.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(ac.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:s,S:re,U:Xt,w:Vt,W:$t,x:c,X:l,y:Wt,Y:Bt,Z:Jt,"%":oe};return t}function It(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(ta.requote).join("|")+")","i")}function Zt(n){for(var t=new l,e=-1,r=n.length;++e68?1900:2e3)}function Kt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=ga(t)/60|0,u=ga(t)%60;return e+It(r,"0",2)+It(u,"0",2)}function oe(n,t,e){hc.lastIndex=0;var r=hc.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);yc.add(Math.atan2(h,f)),r=n,u=c,i=l}var t,e,r,u,i;Mc.point=function(o,a){Mc.point=n,r=(t=o)*Da,u=Math.cos(a=(e=a)*Da/2+qa/4),i=Math.sin(a)},Mc.lineEnd=function(){n(t,e)}}function pe(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Me(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function xe(n){return[Math.atan2(n[1],n[0]),tt(n[2])]}function be(n,t){return ga(n[0]-t[0])a;++a)u.point((e=n[a])[0],e[1]);return void u.lineEnd()}var c=new qe(e,n,null,!0),l=new qe(e,null,c,!1);c.o=l,i.push(c),o.push(l),c=new qe(r,n,null,!1),l=new qe(r,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),ze(i),ze(o),i.length){for(var a=0,c=e,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function ze(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r0){for(b||(i.polygonStart(),b=!0),i.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Te))}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:l,polygonStart:function(){y.point=s,y.lineStart=f,y.lineEnd=h,g=[],p=[]},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=l,g=ta.merge(g);var n=Fe(m,p);g.length?(b||(i.polygonStart(),b=!0),Ce(g,De,n,e,i)):n&&(b||(i.polygonStart(),b=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),b&&(i.polygonEnd(),b=!1),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},M=Re(),x=t(M),b=!1;return y}}function Te(n){return n.length>1}function Re(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function De(n,t){return((n=n.x)[0]<0?n[1]-Ra-Ca:Ra-n[1])-((t=t.x)[0]<0?t[1]-Ra-Ca:Ra-t[1])}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?qa:-qa,c=ga(i-e);ga(c-qa)0?Ra:-Ra),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=qa&&(ga(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*Ra,r.point(-qa,u),r.point(0,u),r.point(qa,u),r.point(qa,0),r.point(qa,-u),r.point(0,-u),r.point(-qa,-u),r.point(-qa,0),r.point(-qa,u);else if(ga(n[0]-t[0])>Ca){var i=n[0]a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+qa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+qa/4,M=Math.sin(y),x=Math.cos(y),b=m-h,_=b>=0?1:-1,w=_*b,S=w>qa,k=p*M;if(yc.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),i+=S?b+_*La:b,S^h>=e^m>=e){var E=de(pe(f),pe(n));Me(E);var A=de(u,E);Me(A);var N=(S^b>=0?-1:1)*tt(A[2]);(r>N||r===N&&(E[0]||E[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=m,p=M,v=x,f=n}}return(-Ca>i||Ca>i&&0>yc)^1&o}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?qa:-qa),h):0;if(!e&&(l=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(be(e,g)||be(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&be(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return s|(l&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),l=a[0],s=c-l*l;if(!s)return!e&&n;var f=i*c/s,h=-i*l/s,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),M=m*m-y*(ve(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=ye(d,(-m-x)/y);if(me(b,p),b=xe(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=ga(A-qa)A;if(!N&&k>E&&(_=k,k=E,E=_),C?N?k+E>0^b[1]<(ga(b[0]-w)qa^(w<=b[0]&&b[0]<=S)){var z=ye(d,(-m+x)/y);return me(z,p),[b,xe(z)]}}}function u(t,e){var r=o?n:qa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ga(i)>Ca,c=gr(n,6*Da);return Le(t,e,c,o?[0,-n]:[-qa,n-qa])}function Oe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Ie(n,t,e,r){function u(r,u){return ga(r[0]-n)0?0:3:ga(r[0]-e)0?2:1:ga(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&Q(l,i,n)>0&&++t:i[1]<=r&&Q(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:e,s>1?r:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,b=_=0/0}function g(){v&&(p(y,M),x&&w&&A.rejoin(),v.push(A.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=s(n,t);if(d&&m.push([n,t]),S)y=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};N(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,m,y,M,x,b,_,w,S,k,E=a,A=Re(),N=Oe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=ta.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Ce(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n){var t=0,e=qa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*qa/180,e=n[1]*qa/180):[t/qa*180,e/qa*180]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,tt((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){Dc+=u*n-r*t,r=n,u=t}var t,e,r,u;Hc.point=function(i,o){Hc.point=n,t=r=i,e=u=o},Hc.lineEnd=function(){n(t,e)}}function Xe(n,t){Pc>n&&(Pc=n),n>jc&&(jc=n),Uc>t&&(Uc=t),t>Fc&&(Fc=t)}function $e(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function We(n,t){_c+=n,wc+=t,++Sc}function Je(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);kc+=o*(t+n)/2,Ec+=o*(e+r)/2,Ac+=o,We(t=n,e=r)}var t,e;Ic.point=function(r,u){Ic.point=n,We(t=r,e=u)}}function Ge(){Ic.point=We}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);kc+=o*(r+n)/2,Ec+=o*(u+t)/2,Ac+=o,o=u*n-r*t,Nc+=o*(r+n),Cc+=o*(u+t),zc+=3*o,We(r=n,u=t)}var t,e,r,u;Ic.point=function(i,o){Ic.point=n,We(t=r=i,e=u=o)},Ic.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,La)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(M,x,y,b,_,w,M=o[0],x=o[1],y=e,b=i[0],_=i[1],w=i[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c +},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,M=f-e,x=y*y+M*M;if(x>4*i&&d--){var b=a+g,_=c+p,w=l+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),E=ga(ga(w)-1)i||ga((y*z+M*q)/x-.5)>.3||o>a*g+c*p+l*v)&&(u(t,e,r,a,c,l,N,C,E,b/=S,_/=S,w,d,m),m.point(N,C),u(N,C,E,b,_,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Da),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Pa,e*Pa])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*Da,n[1]*Da),[n[0]*h+c,l-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Pa,n[1]*Pa]}function r(){a=Ae(o=lr(m,M,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,M=0,x=0,b=Lc,_=y,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=or(b(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Lc):He((w=+n)*Da),u()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Ie(n[0][0],n[0][1],n[1][0],n[1][1]):y,u()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Da,d=n[1]%360*Da,r()):[v*Pa,d*Pa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Da,M=n[1]%360*Da,x=n.length>2?n[2]%360*Da:0,r()):[m*Pa,M*Pa,x*Pa]},ta.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*Da,e*Da)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>qa?n-La:-qa>n?n+La:n,t]}function lr(n,t,e){return n?t||e?Ae(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function sr(n){return function(t,e){return t+=n,[t>qa?t-La:-qa>t?t+La:t,e]}}function fr(n){var t=sr(n);return t.invert=sr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*r+a*u;return[Math.atan2(c*i-s*o,a*r-l*u),tt(s*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*r+s*u),tt(s*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*La)):(u=n+o*La,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=xe([e,-r*Math.cos(s),-r*Math.sin(s)]))[0],l[1])}}function pr(n,t){var e=pe(t);e[0]-=n,Me(e);var r=nt(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function vr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function Mr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(it(r-t)+u*o*it(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*s,u=e*l+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Pa,Math.atan2(o,Math.sqrt(r*r+u*u))*Pa]}:function(){return[n*Pa,t*Pa]};return p.distance=h,p}function xr(){function n(n,u){var i=Math.sin(u*=Da),o=Math.cos(u),a=ga((n*=Da)-t),c=Math.cos(a);Yc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Zc.point=function(u,i){t=u*Da,e=Math.sin(i*=Da),r=Math.cos(i),Zc.point=n},Zc.lineEnd=function(){Zc.point=Zc.lineEnd=b}}function br(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function _r(n,t){function e(n,t){o>0?-Ra+Ca>t&&(t=-Ra+Ca):t>Ra-Ca&&(t=Ra-Ca);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(qa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=K(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ra]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ga(u)u;u++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function zr(n,t){return n[0]-t[0]||n[1]-t[1]}function qr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Lr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],l=e[1],s=t[1]-c,f=r[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Tr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=el.pop()||new Rr;return t.site=n,t}function Pr(n){Xr(n),Qc.remove(n),el.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&ga(e-c.circle.x)s;++s)l=a[s],c=a[s-1],Kr(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=Jr(c.site,l.site,null,u),Vr(c),Vr(l)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Qc._;a;)if(r=Fr(a,o)-i,r>Ca)a=a.L;else{if(u=i-Hr(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Qc.insert(t,c),t||e){if(t===e)return Xr(t),e=Dr(t.site),Qc.insert(c,e),c.edge=e.edge=Jr(t.site,c.site),Vr(t),void Vr(e);if(!e)return void(c.edge=Jr(t.site,c.site));Xr(t),Xr(e);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=e.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,M=v*v+d*d,x={x:(d*y-g*M)/m+s,y:(h*M-v*y)/m+f};Kr(e.edge,l,p,x),c.edge=Jr(l,n,null,x),e.edge=Jr(n,p,null,x),Vr(t),Vr(e)}}function Fr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,l=c-t;if(!l)return a;var s=a-r,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+r:(r+a)/2}function Hr(n,t){var e=n.N;if(e)return Fr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Ir(n){for(var t,e,r,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Kc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),r=s.x,u=s.y,l=a[++o%c].start(),t=l.x,e=l.y,(ga(r-t)>Ca||ga(u-e)>Ca)&&(a.splice(o,0,new Qr(Gr(i.site,s,ga(r-f)Ca?{x:f,y:ga(t-f)Ca?{x:ga(e-p)Ca?{x:h,y:ga(t-h)Ca?{x:ga(e-g)=-za)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=rl.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,M=tl._;M;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};e={x:d,y:l}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/r,y:c};e={x:(l-u)/r,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xi||f>o||r>h||u>g)){if(p=n.point){var p,v=t-n.x,d=e-n.y,m=v*v+d*d;if(c>m){var y=Math.sqrt(c=m);r=t-y,u=e-y,i=t+y,o=e+y,a=p}}for(var M=n.nodes,x=.5*(s+h),b=.5*(f+g),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:l(n,s,f,x,b);break;case 1:l(n,x,f,h,b);break;case 2:l(n,s,b,x,g);break;case 3:l(n,x,b,h,g)}}}(n,r,u,i,o),a}function gu(n,t){n=ta.rgb(n),t=ta.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+xt(Math.round(e+i*n))+xt(Math.round(r+o*n))+xt(Math.round(u+a*n))}}function pu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=mu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function vu(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function du(n,t){var e,r,u,i=il.lastIndex=ol.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(e=il.exec(n))&&(r=ol.exec(t));)(u=r.index)>i&&(u=t.slice(i,u),a[o]?a[o]+=u:a[++o]=u),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,c.push({i:o,x:vu(e,r)})),i=ol.lastIndex;return ir;++r)a[(e=c[r]).i]=e.x(n);return a.join("")})}function mu(n,t){for(var e,r=ta.interpolators.length;--r>=0&&!(e=ta.interpolators[r](n,t)););return e}function yu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(mu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Mu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function bu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function wu(n){return n*n*n}function Su(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function ku(n){return function(t){return Math.pow(t,n)}}function Eu(n){return 1-Math.cos(n*Ra)}function Au(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Cu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/La*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*La/t)}}function zu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function qu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Lu(n,t){n=ta.hcl(n),t=ta.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return st(e+i*n,r+o*n,u+a*n)+""}}function Tu(n,t){n=ta.hsl(n),t=ta.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){n=ta.lab(n),t=ta.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ht(e+i*n,r+o*n,u+a*n)+""}}function Du(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Pu(n){var t=[n.a,n.b],e=[n.c,n.d],r=ju(t),u=Uu(t,e),i=ju(Fu(e,t,-u))||0;t[0]*e[1]180?s+=360:s-l>180&&(l+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:vu(l,s)})):s&&r.push(r.pop()+"rotate("+s+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:vu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:vu(g[0],p[0])},{i:e-2,x:vu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i=0;)e.push(u[r])}function Qu(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(i=n.children)&&(u=i.length))for(var u,i,o=-1;++oe;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function si(n){return n.reduce(fi,0)}function fi(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function pi(n){return[ta.min(n),ta.max(n)]}function vi(n,t){return n.value-t.value}function di(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function mi(n,t){n._pack_next=t,t._pack_prev=n}function yi(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Mi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(xi),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(u=e[1],u.x=u.r,u.y=0,t(u),l>2))for(i=e[2],wi(r,u,i),t(i),di(r,i),r._pack_prev=i,di(i,u),u=r._pack_next,o=3;l>o;o++){wi(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(yi(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!yi(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,M=Math.max(M,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=M,e.forEach(bi)}}function xi(n){n._pack_next=n._pack_prev=n}function bi(n){delete n._pack_next,delete n._pack_prev}function _i(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i=0;)t=u[i],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Ci(n,t,e){return n.a.parent===t.parent?n.a:e}function zi(n){return 1+ta.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Li(n){var t=n.children;return t&&t.length?Li(t[0]):n}function Ti(n){var t,e=n.children;return e&&(t=e.length)?Ti(e[t-1]):n}function Ri(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Di(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function Pi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Pi(n.range())}function ji(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Fi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Hi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ml}function Oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Oi:ji,c=r?Iu:Ou;return o=u(n,t,c,e),a=u(t,n,c,mu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Du)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Xi(n,t)},i.tickFormat=function(t,e){return $i(n,t,e)},i.nice=function(t){return Zi(n,t),u()},i.copy=function(){return Ii(n,t,e,r)},u()}function Yi(n,t){return ta.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Zi(n,t){return Fi(n,Hi(Vi(n,t)[2]))}function Vi(n,t){null==t&&(t=10);var e=Pi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Xi(n,t){return ta.range.apply(ta,Vi(n,t))}function $i(n,t,e){var r=Vi(n,t);if(e){var u=ic.exec(e);if(u.shift(),"s"===u[8]){var i=ta.formatPrefix(Math.max(ga(r[0]),ga(r[1])));return u[7]||(u[7]="."+Bi(i.scale(r[2]))),u[8]="f",e=ta.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Wi(u[8],r)),e=u.join("")}else e=",."+Bi(r[2])+"f";return ta.format(e)}function Bi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Wi(n,t){var e=Bi(t[2]);return n in yl?Math.abs(e-Bi(Math.max(ga(t[0]),ga(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ji(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Fi(r.map(u),e?Math:xl);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=Pi(r),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(e){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return Ml;arguments.length<2?t=Ml:"function"!=typeof t&&(t=ta.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ji(n.copy(),t,e,r)},Yi(o,n)}function Gi(n,t,e){function r(t){return n(u(t))}var u=Ki(t),i=Ki(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Xi(e,n)},r.tickFormat=function(n,t){return $i(e,n,t)},r.nice=function(n){return r.domain(Zi(e,n))},r.exponent=function(o){return arguments.length?(u=Ki(t=o),i=Ki(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Gi(n.copy(),t,e)},Yi(r,n)}function Ki(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Qi(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return ta.range(n.length).map(function(n){return t+e*n})}var u,i,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new l;for(var i,o=-1,a=r.length;++oe?[0/0,0/0]:[e>0?a[e-1]:n[0],et?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return to(n,t,e)},u()}function eo(n,t){function e(e){return e>=e?t[ta.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return eo(n,t)},e}function ro(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Xi(n,t)},t.tickFormat=function(t,e){return $i(n,t,e)},t.copy=function(){return ro(n)},t}function uo(){return 0}function io(n){return n.innerRadius}function oo(n){return n.outerRadius}function ao(n){return n.startAngle}function co(n){return n.endAngle}function lo(n){return n&&n.padAngle}function so(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function fo(n,t,e,r,u){var i=n[0]-t[0],o=n[1]-t[1],a=(u?r:-r)/Math.sqrt(i*i+o*o),c=a*o,l=-a*i,s=n[0]+c,f=n[1]+l,h=t[0]+c,g=t[1]+l,p=(s+h)/2,v=(f+g)/2,d=h-s,m=g-f,y=d*d+m*m,M=e-r,x=s*g-h*f,b=(0>m?-1:1)*Math.sqrt(M*M*y-x*x),_=(x*m-d*b)/y,w=(-x*d-m*b)/y,S=(x*m+d*b)/y,k=(-x*d+m*b)/y,E=_-p,A=w-v,N=S-p,C=k-v;return E*E+A*A>N*N+C*C&&(_=S,w=k),[[_-c,w-l],[_*e/M,w*e/M]]}function ho(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f1&&u.push("H",r[0]),u.join("")}function mo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?go(n):n[0]+_o(n,Lo(n))}function Ro(n){for(var t,e,r,u=-1,i=n.length;++ur)return s();var u=i[i.active];u&&(--i.count,delete i[i.active],u.event&&u.event.interrupt.call(n,n.__data__,u.index)),i.active=r,o.event&&o.event.start.call(n,n.__data__,t),o.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&v.push(r)}),h=o.ease,f=o.duration,ta.timer(function(){return p.c=l(e||1)?Ne:l,1},0,a)}function l(e){if(i.active!==r)return 1;for(var u=e/f,a=h(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,n.__data__,t),s()):void 0}function s(){return--i.count?delete i[r]:delete n[e],1}var f,h,g=o.delay,p=ec,v=[];return p.t=g+a,u>=g?c(u-g):void(p.c=c)},0,a)}}function Bo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function Wo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function Jo(n){return n.toISOString()}function Go(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=ta.bisect(Vl,u);return i==Vl.length?[t.year,Vi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Vl[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Ko(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Ko(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Pi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Ko(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Go(n.copy(),t,e)},Yi(r,n)}function Ko(n){return new Date(n)}function Qo(n){return JSON.parse(n.responseText)}function na(n){var t=ua.createRange();return t.selectNode(ua.body),t.createContextualFragment(n.responseText)}var ta={version:"3.5.5"},ea=[].slice,ra=function(n){return ea.call(n)},ua=this.document;if(ua)try{ra(ua.documentElement.childNodes)[0].nodeType}catch(ia){ra=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),ua)try{ua.createElement("DIV").style.setProperty("opacity",0,"")}catch(oa){var aa=this.Element.prototype,ca=aa.setAttribute,la=aa.setAttributeNS,sa=this.CSSStyleDeclaration.prototype,fa=sa.setProperty;aa.setAttribute=function(n,t){ca.call(this,n,t+"")},aa.setAttributeNS=function(n,t,e){la.call(this,n,t,e+"")},sa.setProperty=function(n,t,e){fa.call(this,n,t+"",e)}}ta.ascending=e,ta.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},ta.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ur&&(e=r)}else{for(;++u=r){e=r;break}for(;++ur&&(e=r)}return e},ta.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ue&&(e=r)}else{for(;++u=r){e=r;break}for(;++ue&&(e=r)}return e},ta.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},ta.sum=function(n,t){var e,r=0,i=n.length,o=-1;if(1===arguments.length)for(;++o1?c/(s-1):void 0},ta.deviation=function(){var n=ta.variance.apply(this,arguments);return n?Math.sqrt(n):n};var ha=i(e);ta.bisectLeft=ha.left,ta.bisect=ta.bisectRight=ha.right,ta.bisector=function(n){return i(1===n.length?function(t,r){return e(n(t),r)}:n)},ta.shuffle=function(n,t,e){(i=arguments.length)<3&&(e=n.length,2>i&&(t=0));for(var r,u,i=e-t;i;)u=Math.random()*i--|0,r=n[i+t],n[i+t]=n[u+t],n[u+t]=r;return n},ta.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ta.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},ta.zip=function(){if(!(r=arguments.length))return[];for(var n=-1,t=ta.min(arguments,o),e=new Array(t);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ga=Math.abs;ta.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,u=[],i=a(ga(e)),o=-1;if(n*=i,t*=i,e*=i,0>e)for(;(r=n+e*++o)>t;)u.push(r/i);else for(;(r=n+e*++o)=i.length)return r?r.call(u,o):e?o.sort(e):o;for(var c,s,f,h,g=-1,p=o.length,v=i[a++],d=new l;++g=i.length)return n;var r=[],u=o[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,u={},i=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(ta.map,e,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return o[i.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},ta.set=function(n){var t=new m;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},c(m,{has:h,add:function(n){return this._[s(n+="")]=!0,n},remove:g,values:p,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,f(t))}}),ta.behavior={},ta.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ta.event=null,ta.requote=function(n){return n.replace(ma,"\\$&")};var ma=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ya={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},Ma=function(n,t){return t.querySelector(n)},xa=function(n,t){return t.querySelectorAll(n)},ba=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(ba=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(Ma=function(n,t){return Sizzle(n,t)[0]||null},xa=Sizzle,ba=Sizzle.matchesSelector),ta.selection=function(){return ta.select(ua.documentElement)};var _a=ta.selection.prototype=[];_a.select=function(n){var t,e,r,u,i=[];n=N(n);for(var o=-1,a=this.length;++o=0&&(e=n.slice(0,t),n=n.slice(t+1)),wa.hasOwnProperty(e)?{space:wa[e],local:n}:n}},_a.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ta.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},_a.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,u=-1;if(t=e.classList){for(;++uu){if("string"!=typeof n){2>u&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>u){var i=this.node();return t(i).getComputedStyle(i,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},_a.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},_a.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},_a.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},_a.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},_a.insert=function(n,t){return n=j(n),t=N(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},_a.remove=function(){return this.each(F)},_a.data=function(n,t){function e(n,e){var r,u,i,o=n.length,f=e.length,h=Math.min(o,f),g=new Array(f),p=new Array(f),v=new Array(o);if(t){var d,m=new l,y=new Array(o);for(r=-1;++rr;++r)p[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,a.push(p),c.push(g),s.push(v)}var r,u,i=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++ii;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return A(u)},_a.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},_a.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},_a.size=function(){var n=0;return Y(this,function(){++n}),n};var Sa=[];ta.selection.enter=Z,ta.selection.enter.prototype=Sa,Sa.append=_a.append,Sa.empty=_a.empty,Sa.node=_a.node,Sa.call=_a.call,Sa.size=_a.size,Sa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var ka=ta.map({mouseenter:"mouseover",mouseleave:"mouseout"});ua&&ka.forEach(function(n){"on"+n in ua&&ka.remove(n)});var Ea,Aa=0;ta.mouse=function(n){return J(n,k())};var Na=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ta.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,u=0,i=t.length;i>u;++u)if((r=t[u]).identifier===e)return J(n,r)},ta.behavior.drag=function(){function n(){this.on("mousedown.drag",i).on("touchstart.drag",o)}function e(n,t,e,i,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],p|=n|e,M=r,g({type:"drag",x:r[0]+l[0],y:r[1]+l[1],dx:n,dy:e}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&ta.event.target===f),g({type:"dragend"}))}var l,s=this,f=ta.event.target,h=s.parentNode,g=r.of(s,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=ta.select(e(f)).on(i+d,a).on(o+d,c),y=W(f),M=t(h,v);u?(l=u.apply(s,arguments),l=[l.x-M[0],l.y-M[1]]):l=[0,0],g({type:"dragstart"})}}var r=E(n,"drag","dragstart","dragend"),u=null,i=e(b,ta.mouse,t,"mousemove","mouseup"),o=e(G,ta.touch,y,"touchmove","touchend");return n.origin=function(t){return arguments.length?(u=t,n):u},ta.rebind(n,r,"on")},ta.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?ra(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Ca=1e-6,za=Ca*Ca,qa=Math.PI,La=2*qa,Ta=La-Ca,Ra=qa/2,Da=qa/180,Pa=180/qa,Ua=Math.SQRT2,ja=2,Fa=4;ta.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=rt(v),o=i/(ja*h)*(e*ut(Ua*t+v)-et(v));return[r+o*l,u+o*s,i*e/rt(Ua*t+v)]}return[r+n*l,u+n*s,i*Math.exp(Ua*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],l=o-r,s=a-u,f=l*l+s*s,h=Math.sqrt(f),g=(c*c-i*i+Fa*f)/(2*i*ja*h),p=(c*c-i*i-Fa*f)/(2*c*ja*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ua;return e.duration=1e3*y,e},ta.behavior.zoom=function(){function n(n){n.on(q,f).on(Oa+".zoom",g).on("dblclick.zoom",p).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function u(n){k.k=Math.max(N[0],Math.min(N[1],n))}function i(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},u(Math.pow(2,o)),i(d=e,r),t=ta.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function c(n){z++||n({type:"zoomstart"})}function l(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function s(n){--z||n({type:"zoomend"}),d=null}function f(){function n(){f=1,i(ta.mouse(u),g),l(a)}function r(){h.on(L,null).on(T,null),p(f&&ta.event.target===o),s(a)}var u=this,o=ta.event.target,a=D.of(u,arguments),f=0,h=ta.select(t(u)).on(L,n).on(T,r),g=e(ta.mouse(u)),p=W(u);Dl.call(u),c(a)}function h(){function n(){var n=ta.touches(p);return g=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ta.event.target;ta.select(t).on(x,r).on(b,a),_.push(t);for(var e=ta.event.changedTouches,u=0,i=e.length;i>u;++u)d[e[u].identifier]=null;var c=n(),l=Date.now();if(1===c.length){if(500>l-M){var s=c[0];o(p,s,d[s.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=l}else if(c.length>1){var s=c[0],f=c[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function r(){var n,t,e,r,o=ta.touches(p);Dl.call(p);for(var a=0,c=o.length;c>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var s=(s=e[0]-n[0])*s+(s=e[1]-n[1])*s,f=m&&Math.sqrt(s/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],u(f*g)}M=null,i(n,t),l(v)}function a(){if(ta.event.touches.length){for(var t=ta.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}ta.selectAll(_).on(y,null),w.on(q,f).on(R,h),E(),s(v)}var g,p=this,v=D.of(p,arguments),d={},m=0,y=".zoom-"+ta.event.changedTouches[0].identifier,x="touchmove"+y,b="touchend"+y,_=[],w=ta.select(p),E=W(p);t(),c(v),w.on(q,null).on(R,t)}function g(){var n=D.of(this,arguments);y?clearTimeout(y):(v=e(d=m||ta.mouse(this)),Dl.call(this),c(n)),y=setTimeout(function(){y=null,s(n)},50),S(),u(Math.pow(2,.002*Ha())*k.k),i(d,v),l(n)}function p(){var n=ta.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ta.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,m,y,M,x,b,_,w,k={x:0,y:0,k:1},A=[960,500],N=Ia,C=250,z=0,q="mousedown.zoom",L="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=E(n,"zoomstart","zoom","zoomend");return Oa||(Oa="onwheel"in ua?(Ha=function(){return-ta.event.deltaY*(ta.event.deltaMode?120:1)},"wheel"):"onmousewheel"in ua?(Ha=function(){return ta.event.wheelDelta},"mousewheel"):(Ha=function(){return-ta.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Tl?ta.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},c(n)}).tween("zoom:zoom",function(){var e=A[0],r=A[1],u=d?d[0]:e/2,i=d?d[1]:r/2,o=ta.interpolateZoom([(u-k.x)/k.k,(i-k.y)/k.k,e/k.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:u-r[0]*a,y:i-r[1]*a,k:a},l(n)}}).each("interrupt.zoom",function(){s(n)}).each("end.zoom",function(){s(n)}):(this.__chart__=k,c(n),l(n),s(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:+t},a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(N=null==t?Ia:[+t[0],+t[1]],n):N},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(A=t&&[+t[0],+t[1]],n):A},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ta.rebind(n,D,"on")};var Ha,Oa,Ia=[0,1/0];ta.color=ot,ot.prototype.toString=function(){return this.rgb()+""},ta.hsl=at;var Ya=at.prototype=new ot;Ya.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,this.l/n)},Ya.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,n*this.l)},Ya.rgb=function(){return ct(this.h,this.s,this.l)},ta.hcl=lt;var Za=lt.prototype=new ot;Za.brighter=function(n){return new lt(this.h,this.c,Math.min(100,this.l+Va*(arguments.length?n:1)))},Za.darker=function(n){return new lt(this.h,this.c,Math.max(0,this.l-Va*(arguments.length?n:1)))},Za.rgb=function(){return st(this.h,this.c,this.l).rgb()},ta.lab=ft;var Va=18,Xa=.95047,$a=1,Ba=1.08883,Wa=ft.prototype=new ot;Wa.brighter=function(n){return new ft(Math.min(100,this.l+Va*(arguments.length?n:1)),this.a,this.b)},Wa.darker=function(n){return new ft(Math.max(0,this.l-Va*(arguments.length?n:1)),this.a,this.b)},Wa.rgb=function(){return ht(this.l,this.a,this.b)},ta.rgb=mt;var Ja=mt.prototype=new ot;Ja.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),new mt(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mt(u,u,u)},Ja.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mt(n*this.r,n*this.g,n*this.b)},Ja.hsl=function(){return _t(this.r,this.g,this.b)},Ja.toString=function(){return"#"+xt(this.r)+xt(this.g)+xt(this.b)};var Ga=ta.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Ga.forEach(function(n,t){Ga.set(n,yt(t))}),ta.functor=Et,ta.xhr=At(y),ta.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=Nt(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(s>=l)return o;if(u)return u=!1,i;var t=s;if(34===n.charCodeAt(t)){for(var e=t;e++s;){var r=n.charCodeAt(s++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(s)&&(++s,++a);else if(r!==c)continue;return n.slice(t,s-a)}return n.slice(t)}for(var r,u,i={},o={},a=[],l=n.length,s=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,f++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new m,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},ta.csv=ta.dsv(",","text/csv"),ta.tsv=ta.dsv(" ","text/tab-separated-values");var Ka,Qa,nc,tc,ec,rc=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ta.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Qa?Qa.n=i:Ka=i,Qa=i,nc||(tc=clearTimeout(tc),nc=1,rc(qt))},ta.timer.flush=function(){Lt(),Tt()},ta.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var uc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);ta.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=ta.round(n,Rt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),uc[8+e/3]};var ic=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,oc=ta.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ta.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),ac=ta.time={},cc=Date;jt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){lc.setUTCDate.apply(this._,arguments)},setDay:function(){lc.setUTCDay.apply(this._,arguments)},setFullYear:function(){lc.setUTCFullYear.apply(this._,arguments)},setHours:function(){lc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){lc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){lc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){lc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){lc.setUTCSeconds.apply(this._,arguments)},setTime:function(){lc.setTime.apply(this._,arguments)}};var lc=Date.prototype;ac.year=Ft(function(n){return n=ac.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ac.years=ac.year.range,ac.years.utc=ac.year.utc.range,ac.day=Ft(function(n){var t=new cc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ac.days=ac.day.range,ac.days.utc=ac.day.utc.range,ac.dayOfYear=function(n){var t=ac.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ac[n]=Ft(function(n){return(n=ac.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ac[n+"s"]=e.range,ac[n+"s"].utc=e.utc.range,ac[n+"OfYear"]=function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)}}),ac.week=ac.sunday,ac.weeks=ac.sunday.range,ac.weeks.utc=ac.sunday.utc.range,ac.weekOfYear=ac.sundayOfYear;var sc={"-":"",_:" ",0:"0"},fc=/^\s*\d+/,hc=/^%/;ta.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var gc=ta.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ta.format=gc.numberFormat,ta.geo={},ce.prototype={s:0,t:0,add:function(n){le(n,this.t,pc),le(pc.s,this.s,this),this.s?this.t+=pc.t:this.s=pc.t +},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var pc=new ce;ta.geo.stream=function(n,t){n&&vc.hasOwnProperty(n.type)?vc[n.type](n,t):se(n,t)};var vc={Feature:function(n,t){se(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*qa+n:n,Mc.lineStart=Mc.lineEnd=Mc.point=b}};ta.geo.bounds=function(){function n(n,t){M.push(x=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=pe([t*Da,e*Da]);if(m){var u=de(m,r),i=[u[1],-u[0],0],o=de(i,u);Me(o),o=xe(o);var c=t-p,l=c>0?1:-1,v=o[0]*Pa*l,d=ga(c)>180;if(d^(v>l*p&&l*t>v)){var y=o[1]*Pa;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>l*p&&l*t>v)){var y=-o[1]*Pa;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t)}else n(t,e);m=r,p=t}function e(){b.point=t}function r(){x[0]=s,x[1]=h,b.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=ga(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Mc.point(n,e),t(n,e)}function i(){Mc.lineStart()}function o(){u(v,d),Mc.lineEnd(),ga(y)>Ca&&(s=-(h=180)),x[0]=s,x[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nyc?(s=-(h=180),f=-(g=90)):y>Ca?g=90:-Ca>y&&(f=-90),x[0]=s,x[1]=h}};return function(n){g=h=-(s=f=1/0),M=[],ta.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,u=M[0],i=[u];t>r;++r)e=M[r],l(e[0],u)||l(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,s=e[0],h=u[1])}return M=x=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),ta.geo.centroid=function(n){xc=bc=_c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,qc);var t=Nc,e=Cc,r=zc,u=t*t+e*e+r*r;return za>u&&(t=kc,e=Ec,r=Ac,Ca>bc&&(t=_c,e=wc,r=Sc),u=t*t+e*e+r*r,za>u)?[0/0,0/0]:[Math.atan2(e,t)*Pa,tt(r/Math.sqrt(u))*Pa]};var xc,bc,_c,wc,Sc,kc,Ec,Ac,Nc,Cc,zc,qc={sphere:b,point:_e,lineStart:Se,lineEnd:ke,polygonStart:function(){qc.lineStart=Ee},polygonEnd:function(){qc.lineStart=Se}},Lc=Le(Ne,Pe,je,[-qa,-qa/2]),Tc=1e9;ta.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Ie(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ta.geo.conicEqualArea=function(){return Ye(Ze)}).raw=Ze,ta.geo.albers=function(){return ta.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ta.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=ta.geo.albers(),o=ta.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ta.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var l=i.scale(),s=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,r=o.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Ca,f+.12*l+Ca],[s-.214*l-Ca,f+.234*l-Ca]]).stream(c).point,u=a.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Ca,f+.166*l+Ca],[s-.115*l-Ca,f+.234*l-Ca]]).stream(c).point,n},n.scale(1070)};var Rc,Dc,Pc,Uc,jc,Fc,Hc={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Dc=0,Hc.lineStart=Ve},polygonEnd:function(){Hc.lineStart=Hc.lineEnd=Hc.point=b,Rc+=ga(Dc/2)}},Oc={point:Xe,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Ic={point:We,lineStart:Je,lineEnd:Ge,polygonStart:function(){Ic.lineStart=Ke},polygonEnd:function(){Ic.point=We,Ic.lineStart=Je,Ic.lineEnd=Ge}};ta.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),ta.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Rc=0,ta.geo.stream(n,u(Hc)),Rc},n.centroid=function(n){return _c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,u(Ic)),zc?[Nc/zc,Cc/zc]:Ac?[kc/Ac,Ec/Ac]:Sc?[_c/Sc,wc/Sc]:[0/0,0/0]},n.bounds=function(n){return jc=Fc=-(Pc=Uc=1/0),ta.geo.stream(n,u(Oc)),[[Pc,Uc],[jc,Fc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||tr(n):y,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new $e:new Qe(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(ta.geo.albersUsa()).context(null)},ta.geo.transform=function(n){return{stream:function(t){var e=new er(t);for(var r in n)e[r]=n[r];return e}}},er.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ta.geo.projection=ur,ta.geo.projectionMutator=ir,(ta.geo.equirectangular=function(){return ur(ar)}).raw=ar.invert=ar,ta.geo.rotation=function(n){function t(t){return t=n(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t}return n=lr(n[0]%360*Da,n[1]*Da,n.length>2?n[2]*Da:0),t.invert=function(t){return t=n.invert(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t},t},cr.invert=ar,ta.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=lr(-n[0]*Da,-n[1]*Da,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Pa,n[1]*=Pa}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=gr((t=+r)*Da,u*Da),n):t},n.precision=function(r){return arguments.length?(e=gr(t*Da,(u=+r)*Da),n):u},n.angle(90)},ta.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Da,u=n[1]*Da,i=t[1]*Da,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),l=Math.cos(u),s=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=l*s-c*f*a)*e),c*s+l*f*a)},ta.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ta.range(Math.ceil(i/d)*d,u,d).map(h).concat(ta.range(Math.ceil(l/m)*m,c,m).map(g)).concat(ta.range(Math.ceil(r/p)*p,e,p).filter(function(n){return ga(n%d)>Ca}).map(s)).concat(ta.range(Math.ceil(a/v)*v,o,v).filter(function(n){return ga(n%m)>Ca}).map(f))}var e,r,u,i,o,a,c,l,s,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],l=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[i,l],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,s=vr(a,o,90),f=dr(r,e,y),h=vr(l,c,90),g=dr(i,u,y),n):y},n.majorExtent([[-180,-90+Ca],[180,90-Ca]]).minorExtent([[-180,-80-Ca],[180,80+Ca]])},ta.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=mr,u=yr;return n.distance=function(){return ta.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},ta.geo.interpolate=function(n,t){return Mr(n[0]*Da,n[1]*Da,t[0]*Da,t[1]*Da)},ta.geo.length=function(n){return Yc=0,ta.geo.stream(n,Zc),Yc};var Yc,Zc={sphere:b,point:b,lineStart:xr,lineEnd:b,polygonStart:b,polygonEnd:b},Vc=br(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ta.geo.azimuthalEqualArea=function(){return ur(Vc)}).raw=Vc;var Xc=br(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},y);(ta.geo.azimuthalEquidistant=function(){return ur(Xc)}).raw=Xc,(ta.geo.conicConformal=function(){return Ye(_r)}).raw=_r,(ta.geo.conicEquidistant=function(){return Ye(wr)}).raw=wr;var $c=br(function(n){return 1/n},Math.atan);(ta.geo.gnomonic=function(){return ur($c)}).raw=$c,Sr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ra]},(ta.geo.mercator=function(){return kr(Sr)}).raw=Sr;var Bc=br(function(){return 1},Math.asin);(ta.geo.orthographic=function(){return ur(Bc)}).raw=Bc;var Wc=br(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ta.geo.stereographic=function(){return ur(Wc)}).raw=Wc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ra]},(ta.geo.transverseMercator=function(){var n=kr(Er),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Er,ta.geom={},ta.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(e),i=Et(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(zr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=Cr(a),s=Cr(c),f=s[0]===l[0],h=s[s.length-1]===l[l.length-1],g=[];for(t=l.length-1;t>=0;--t)g.push(n[a[l[t]][2]]);for(t=+f;t=r&&l.x<=i&&l.y>=u&&l.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];s.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ca)*Ca,y:Math.round(o(n,t)/Ca)*Ca,i:t}})}var r=Ar,u=Nr,i=r,o=u,a=ul;return n?t(n):(t.links=function(n){return iu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return iu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(Yr),c=-1,l=a.length,s=a[l-1].edge,f=s.l===o?s.r:s.l;++c=l,h=r>=s,g=h<<1|f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=su()),f?u=l:a=l,h?o=s:c=s,i(n,t,e,r,u,o,a,c)}var s,f,h,g,p,v,d,m,y,M=Et(a),x=Et(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)s=n[g],s.xm&&(m=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var b=+M(s=n[g],g),_=+x(s,g);v>b&&(v=b),d>_&&(d=_),b>m&&(m=b),_>y&&(y=_),f.push(b),h.push(_)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=su();if(k.add=function(n){i(k,n,+M(n,++g),+x(n,g),v,d,m,y)},k.visit=function(n){fu(n,k,v,d,m,y)},k.find=function(n){return hu(k,n[0],n[1],v,d,m,y)},g=-1,null==t){for(;++g=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=cl.get(e)||al,r=ll.get(r)||y,Mu(r(e.apply(null,ea.call(arguments,1))))},ta.interpolateHcl=Lu,ta.interpolateHsl=Tu,ta.interpolateLab=Ru,ta.interpolateRound=Du,ta.transform=function(n){var t=ua.createElementNS(ta.ns.prefix.svg,"g");return(ta.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Pu(e?e.matrix:sl)})(n)},Pu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var sl={a:1,b:0,c:0,d:1,e:0,f:0};ta.interpolateTransform=Hu,ta.layout={},ta.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/d){if(p>c){var l=t.charge/c;n.px-=i*l,n.py-=o*l}return!0}if(t.point&&c&&p>c){var l=t.pointCharge/c;n.px-=i*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=ta.event.x,n.py=ta.event.y,a.resume()}var e,r,u,i,o,a={},c=ta.dispatch("start","tick","end"),l=[1,1],s=.9,f=fl,h=hl,g=-30,p=gl,v=.1,d=.64,m=[],M=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,y,x,b=m.length,_=M.length;for(e=0;_>e;++e)a=M[e],f=a.source,h=a.target,y=h.x-f.x,x=h.y-f.y,(p=y*y+x*x)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,y*=p,x*=p,h.x-=y*(d=f.weight/(h.weight+f.weight)),h.y-=x*d,f.x+=y*(d=1-d),f.y+=x*d);if((d=r*v)&&(y=l[0]/2,x=l[1]/2,e=-1,d))for(;++e0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),ta.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=M[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,l=o.length;++at;++t)(r=m[t]).index=t,r.weight=0;for(t=0;s>t;++t)r=M[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;s>t;++t)u[t]=+f.call(this,M[t],t);else for(t=0;s>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;s>t;++t)i[t]=+h.call(this,M[t],t);else for(t=0;s>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=ta.behavior.drag().origin(y).on("dragstart.force",Xu).on("drag.force",t).on("dragend.force",$u)),arguments.length?void this.on("mouseover.force",Bu).on("mouseout.force",Wu).call(e):e},ta.rebind(a,c,"on")};var fl=20,hl=1,gl=1/0;ta.layout.hierarchy=function(){function n(u){var i,o=[u],a=[];for(u.depth=0;null!=(i=o.pop());)if(a.push(i),(l=e.call(n,i,i.depth))&&(c=l.length)){for(var c,l,s;--c>=0;)o.push(s=l[c]),s.parent=i,s.depth=i.depth+1;r&&(i.value=0),i.children=l}else r&&(i.value=+r.call(n,i,i.depth)||0),delete i.children;return Qu(u,function(n){var e,u;t&&(e=n.children)&&e.sort(t),r&&(u=n.parent)&&(u.value+=n.value)}),a}var t=ei,e=ni,r=ti;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(Ku(t,function(n){n.children&&(n.value=0)}),Qu(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ta.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,l=-1;for(r=t.value?r/t.value:0;++lf?-1:1),p=(f-c*g)/ta.sum(l),v=ta.range(c),d=[];return null!=e&&v.sort(e===pl?function(n,t){return l[t]-l[n]}:function(n,t){return e(o[n],o[t])}),v.forEach(function(n){d[n]={data:o[n],value:a=l[n],startAngle:s,endAngle:s+=a*p+g,padAngle:h}}),d}var t=Number,e=pl,r=0,u=La,i=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n.padAngle=function(t){return arguments.length?(i=t,n):i},n};var pl={};ta.layout.stack=function(){function n(a,c){if(!(h=a.length))return a;var l=a.map(function(e,r){return t.call(n,e,r)}),s=l.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,s,c);l=ta.permute(l,f),s=ta.permute(s,f);var h,g,p,v,d=r.call(n,s,c),m=l[0].length;for(p=0;m>p;++p)for(u.call(n,l[0][p],v=d[p],s[0][p][1]),g=1;h>g;++g)u.call(n,l[g][p],v+=s[g-1][p][1],s[g][p][1]);return a}var t=y,e=ai,r=ci,u=oi,i=ui,o=ii;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:vl.get(t)||ai,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:dl.get(t)||ci,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var vl=ta.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(li),i=n.map(si),o=ta.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,l=[],s=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],l.push(e)):(c+=i[e],s.push(e));return s.reverse().concat(l)},reverse:function(n){return ta.range(n.length).reverse()},"default":ai}),dl=ta.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,u=0;s>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];s>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ci});ta.layout.histogram=function(){function n(n,i){for(var o,a,c=[],l=n.map(e,this),s=r.call(this,l,i),f=u.call(this,s,l,i),i=-1,h=l.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=s[0]&&a<=s[1]&&(o=c[ta.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=pi,u=hi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=Et(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return gi(n,t)}:Et(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ta.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],l=u[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Qu(a,function(n){n.r=+s(n.value)}),Qu(a,Mi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;Qu(a,function(n){n.r+=f}),Qu(a,Mi),Qu(a,function(n){n.r-=f})}return _i(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,e=ta.layout.hierarchy().sort(vi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Gu(n,e)},ta.layout.tree=function(){function n(n,u){var s=o.call(this,n,u),f=s[0],h=t(f);if(Qu(h,e),h.parent.m=-h.z,Ku(h,r),l)Ku(f,i);else{var g=f,p=f,v=f;Ku(f,function(n){n.xp.x&&(p=n),n.depth>v.depth&&(v=n)});var d=a(g,p)/2-g.x,m=c[0]/(p.x+a(p,g)/2+d),y=c[1]/(v.depth||1);Ku(f,function(n){n.x=(n.x+d)*m,n.y=n.depth*y})}return s}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var u,i=t.children,o=0,a=i.length;a>o;++o)r.push((i[o]=u={_:i[o],parent:t,children:(u=i[o].children)&&u.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=u);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Ni(n);var i=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-i):n.z=i}else r&&(n.z=r.z+a(n._,r._));n.parent.A=u(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function u(n,t,e){if(t){for(var r,u=n,i=n,o=t,c=u.parent.children[0],l=u.m,s=i.m,f=o.m,h=c.m;o=Ei(o),u=ki(u),o&&u;)c=ki(c),i=Ei(i),i.a=n,r=o.z+f-u.z-l+a(o._,u._),r>0&&(Ai(Ci(o,n,e),n,r),l+=r,s+=r),f+=o.m,l+=u.m,h+=c.m,s+=i.m;o&&!Ei(i)&&(i.t=o,i.m+=f-s),u&&!ki(c)&&(c.t=u,c.m+=l-h,e=n)}return e}function i(n){n.x*=c[0],n.y=n.depth*c[1]}var o=ta.layout.hierarchy().sort(null).value(null),a=Si,c=[1,1],l=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(l=null==(c=t)?i:null,n):l?null:c},n.nodeSize=function(t){return arguments.length?(l=null==(c=t)?null:i,n):l?c:null},Gu(n,o)},ta.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],l=0;Qu(c,function(n){var t=n.children;t&&t.length?(n.x=qi(t),n.y=zi(t)):(n.x=o?l+=e(n,o):0,n.y=0,o=n)});var s=Li(c),f=Ti(c),h=s.x-e(s,f)/2,g=f.x+e(f,s)/2;return Qu(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=ta.layout.hierarchy().sort(null).value(null),e=Si,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Gu(n,t)},ta.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++ut?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,l=f(e),s=[],h=i.slice(),p=1/0,v="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),s.area=0;(c=h.length)>0;)s.push(o=h[c-1]),s.area+=o.area,"squarify"!==g||(a=r(s,v))<=p?(h.pop(),p=a):(s.area-=s.pop().area,u(s,v,l,!1),v=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(u(s,v,l,!0),s.length=s.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++oe&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,l=e.y,s=t?c(n.area/t):0;if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++ie.dx)&&(s=e.dx);++ie&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=ta.random.normal.apply(ta,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ta.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ta.scale={};var ml={floor:y,ceil:y};ta.scale.linear=function(){return Ii([0,1],[0,1],mu,!1)};var yl={s:1,g:1,p:1,r:1,e:1};ta.scale.log=function(){return Ji(ta.scale.linear().domain([0,1]),10,!0,[1,10])};var Ml=ta.format(".0e"),xl={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ta.scale.pow=function(){return Gi(ta.scale.linear(),1,[0,1])},ta.scale.sqrt=function(){return ta.scale.pow().exponent(.5)},ta.scale.ordinal=function(){return Qi([],{t:"range",a:[[]]})},ta.scale.category10=function(){return ta.scale.ordinal().range(bl)},ta.scale.category20=function(){return ta.scale.ordinal().range(_l)},ta.scale.category20b=function(){return ta.scale.ordinal().range(wl)},ta.scale.category20c=function(){return ta.scale.ordinal().range(Sl)};var bl=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(Mt),_l=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(Mt),wl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(Mt),Sl=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(Mt);ta.scale.quantile=function(){return no([],[])},ta.scale.quantize=function(){return to(0,1,[0,1])},ta.scale.threshold=function(){return eo([.5],[0,1])},ta.scale.identity=function(){return ro([0,1])},ta.svg={},ta.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),l=Math.max(0,+r.apply(this,arguments)),s=o.apply(this,arguments)-Ra,f=a.apply(this,arguments)-Ra,h=Math.abs(f-s),g=s>f?0:1;if(n>l&&(p=l,l=n,n=p),h>=Ta)return t(l,g)+(n?t(n,1-g):"")+"Z";var p,v,d,m,y,M,x,b,_,w,S,k,E=0,A=0,N=[];if((m=(+c.apply(this,arguments)||0)/2)&&(d=i===kl?Math.sqrt(n*n+l*l):+i.apply(this,arguments),g||(A*=-1),l&&(A=tt(d/l*Math.sin(m))),n&&(E=tt(d/n*Math.sin(m)))),l){y=l*Math.cos(s+A),M=l*Math.sin(s+A),x=l*Math.cos(f-A),b=l*Math.sin(f-A);var C=Math.abs(f-s-2*A)<=qa?0:1;if(A&&so(y,M,x,b)===g^C){var z=(s+f)/2;y=l*Math.cos(z),M=l*Math.sin(z),x=b=null}}else y=M=0;if(n){_=n*Math.cos(f-E),w=n*Math.sin(f-E),S=n*Math.cos(s+E),k=n*Math.sin(s+E);var q=Math.abs(s-f+2*E)<=qa?0:1;if(E&&so(_,w,S,k)===1-g^q){var L=(s+f)/2;_=n*Math.cos(L),w=n*Math.sin(L),S=k=null}}else _=w=0;if((p=Math.min(Math.abs(l-n)/2,+u.apply(this,arguments)))>.001){v=l>n^g?0:1;var T=null==S?[_,w]:null==x?[y,M]:Lr([y,M],[S,k],[x,b],[_,w]),R=y-T[0],D=M-T[1],P=x-T[0],U=b-T[1],j=1/Math.sin(Math.acos((R*P+D*U)/(Math.sqrt(R*R+D*D)*Math.sqrt(P*P+U*U)))/2),F=Math.sqrt(T[0]*T[0]+T[1]*T[1]);if(null!=x){var H=Math.min(p,(l-F)/(j+1)),O=fo(null==S?[_,w]:[S,k],[y,M],l,H,g),I=fo([x,b],[_,w],l,H,g);p===H?N.push("M",O[0],"A",H,",",H," 0 0,",v," ",O[1],"A",l,",",l," 0 ",1-g^so(O[1][0],O[1][1],I[1][0],I[1][1]),",",g," ",I[1],"A",H,",",H," 0 0,",v," ",I[0]):N.push("M",O[0],"A",H,",",H," 0 1,",v," ",I[0])}else N.push("M",y,",",M);if(null!=S){var Y=Math.min(p,(n-F)/(j-1)),Z=fo([y,M],[S,k],n,-Y,g),V=fo([_,w],null==x?[y,M]:[x,b],n,-Y,g);p===Y?N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",V[1],"A",n,",",n," 0 ",g^so(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-g," ",Z[1],"A",Y,",",Y," 0 0,",v," ",Z[0]):N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",Z[0])}else N.push("L",_,",",w)}else N.push("M",y,",",M),null!=x&&N.push("A",l,",",l," 0 ",C,",",g," ",x,",",b),N.push("L",_,",",w),null!=S&&N.push("A",n,",",n," 0 ",q,",",1-g," ",S,",",k);return N.push("Z"),N.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=io,r=oo,u=uo,i=kl,o=ao,a=co,c=lo;return n.innerRadius=function(t){return arguments.length?(e=Et(t),n):e},n.outerRadius=function(t){return arguments.length?(r=Et(t),n):r},n.cornerRadius=function(t){return arguments.length?(u=Et(t),n):u},n.padRadius=function(t){return arguments.length?(i=t==kl?kl:Et(t),n):i},n.startAngle=function(t){return arguments.length?(o=Et(t),n):o},n.endAngle=function(t){return arguments.length?(a=Et(t),n):a},n.padAngle=function(t){return arguments.length?(c=Et(t),n):c},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Ra;return[Math.cos(t)*n,Math.sin(t)*n]},n};var kl="auto";ta.svg.line=function(){return ho(y)};var El=ta.map({linear:go,"linear-closed":po,step:vo,"step-before":mo,"step-after":yo,basis:So,"basis-open":ko,"basis-closed":Eo,bundle:Ao,cardinal:bo,"cardinal-open":Mo,"cardinal-closed":xo,monotone:To});El.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Al=[0,2/3,1/3,0],Nl=[0,1/3,2/3,0],Cl=[0,1/6,2/3,1/6];ta.svg.line.radial=function(){var n=ho(Ro);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},mo.reverse=yo,yo.reverse=mo,ta.svg.area=function(){return Do(y)},ta.svg.area.radial=function(){var n=Do(Ro);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ta.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),l=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+u(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)-Ra,s=l.call(n,u,r)-Ra;return{r:i,a0:o,a1:s,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(s),i*Math.sin(s)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>qa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=mr,o=yr,a=Po,c=ao,l=co;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(l=Et(t),n):l},n},ta.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=mr,e=yr,r=Uo;return n.source=function(e){return arguments.length?(t=Et(e),n):t},n.target=function(t){return arguments.length?(e=Et(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ta.svg.diagonal.radial=function(){var n=ta.svg.diagonal(),t=Uo,e=n.projection;return n.projection=function(n){return arguments.length?e(jo(t=n)):t},n},ta.svg.symbol=function(){function n(n,r){return(zl.get(t.call(this,n,r))||Oo)(e.call(this,n,r))}var t=Ho,e=Fo;return n.type=function(e){return arguments.length?(t=Et(e),n):t},n.size=function(t){return arguments.length?(e=Et(t),n):e},n};var zl=ta.map({circle:Oo,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Ll)),e=t*Ll;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ta.svg.symbolTypes=zl.keys();var ql=Math.sqrt(3),Ll=Math.tan(30*Da);_a.transition=function(n){for(var t,e,r=Tl||++Ul,u=Xo(n),i=[],o=Rl||{time:Date.now(),ease:Su,delay:0,duration:250},a=-1,c=this.length;++ai;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Yo(u,this.namespace,this.id)},Pl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(u){u[r][e].tween.set(n,t)})},Pl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Hu:mu,a=ta.ns.qualify(n);return Zo(this,"attr."+n,t,a.local?i:u)},Pl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=ta.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Pl.style=function(n,e,r){function u(){this.style.removeProperty(n)}function i(e){return null==e?u:(e+="",function(){var u,i=t(this).getComputedStyle(this,null).getPropertyValue(n);return i!==e&&(u=mu(i,e),function(t){this.style.setProperty(n,u(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Zo(this,"style."+n,e,i)},Pl.styleTween=function(n,e,r){function u(u,i){var o=e.call(this,u,i,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,u)},Pl.text=function(n){return Zo(this,"text",n,Vo)},Pl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Pl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ta.ease.apply(ta,arguments)),Y(this,function(r){r[e][t].ease=n}))},Pl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,u,i){r[e][t].delay=+n.call(r,r.__data__,u,i)}:(n=+n,function(r){r[e][t].delay=n}))},Pl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,u,i){r[e][t].duration=Math.max(1,n.call(r,r.__data__,u,i))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Pl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var u=Rl,i=Tl;try{Tl=e,Y(this,function(t,u,i){Rl=t[r][e],n.call(t,t.__data__,u,i)})}finally{Rl=u,Tl=i}}else Y(this,function(u){var i=u[r][e];(i.event||(i.event=ta.dispatch("start","end","interrupt"))).on(n,t)});return this},Pl.transition=function(){for(var n,t,e,r,u=this.id,i=++Ul,o=this.namespace,a=[],c=0,l=this.length;l>c;c++){a.push(n=[]);for(var t=this[c],s=0,f=t.length;f>s;s++)(e=t[s])&&(r=e[o][u],$o(e,s,o,i,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Yo(a,o,i)},ta.svg.axis=function(){function n(n){n.each(function(){var n,l=ta.select(this),s=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):y:t,p=l.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ca),d=ta.transition(p.exit()).style("opacity",Ca).remove(),m=ta.transition(p.order()).style("opacity",1),M=Math.max(u,0)+o,x=Ui(f),b=l.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ta.transition(b));v.append("line"),v.append("text");var w,S,k,E,A=v.select("line"),N=m.select("line"),C=p.select("text").text(g),z=v.select("text"),q=m.select("text"),L="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=Bo,w="x",k="y",S="x2",E="y2",C.attr("dy",0>L?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+L*i+"V0H"+x[1]+"V"+L*i)):(n=Wo,w="y",k="x",S="y2",E="x2",C.attr("dy",".32em").style("text-anchor",0>L?"end":"start"),_.attr("d","M"+L*i+","+x[0]+"H0V"+x[1]+"H"+L*i)),A.attr(E,L*u),z.attr(k,L*M),N.attr(S,0).attr(E,L*u),q.attr(w,0).attr(k,L*M),f.rangeBand){var T=f,R=T.rangeBand()/2;s=f=function(n){return T(n)+R}}else s.rangeBand?s=f:d.call(n,f,s);v.call(n,s,f),m.call(n,f,f)})}var t,e=ta.scale.linear(),r=jl,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Fl?t+"":jl,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var jl="bottom",Fl={top:1,right:1,bottom:1,left:1};ta.svg.brush=function(){function n(t){t.each(function(){var t=ta.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",i).on("touchstart.brush",i),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,y);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Hl[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var c,f=ta.transition(t),h=ta.transition(o);l&&(c=Ui(l),h.attr("x",c[0]).attr("width",c[1]-c[0]),r(f)),s&&(c=Ui(s),h.attr("y",c[0]).attr("height",c[1]-c[0]),u(f)),e(f)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+f[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",f[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1]-f[0])}function u(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function i(){function i(){32==ta.event.keyCode&&(C||(M=null,q[0]-=f[1],q[1]-=h[1],C=2),S())}function v(){32==ta.event.keyCode&&2==C&&(q[0]+=f[1],q[1]+=h[1],C=0,S())}function d(){var n=ta.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ta.event.altKey?(M||(M=[(f[0]+f[1])/2,(h[0]+h[1])/2]),q[0]=f[+(n[0]s?(u=r,r=s):u=s),v[0]!=r||v[1]!=u?(e?a=null:o=null,v[0]=r,v[1]=u,!0):void 0}function y(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ta.select("body").style("cursor",null),L.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ta.select(ta.event.target),w=c.of(b,arguments),k=ta.select(b),E=_.datum(),A=!/^(n|s)$/.test(E)&&l,N=!/^(e|w)$/.test(E)&&s,C=_.classed("extent"),z=W(b),q=ta.mouse(b),L=ta.select(t(b)).on("keydown.brush",i).on("keyup.brush",v);if(ta.event.changedTouches?L.on("touchmove.brush",d).on("touchend.brush",y):L.on("mousemove.brush",d).on("mouseup.brush",y),k.interrupt().selectAll("*").interrupt(),C)q[0]=f[0]-q[0],q[1]=h[0]-q[1];else if(E){var T=+/w$/.test(E),R=+/^n/.test(E);x=[f[1-T]-q[0],h[1-R]-q[1]],q[0]=f[T],q[1]=h[R]}else ta.event.altKey&&(M=q.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ta.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,c=E(n,"brushstart","brush","brushend"),l=null,s=null,f=[0,0],h=[0,0],g=!0,p=!0,v=Ol[0];return n.event=function(n){n.each(function(){var n=c.of(this,arguments),t={x:f,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Tl?ta.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,f=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=yu(f,t.x),r=yu(h,t.y);return o=a=null,function(u){f=t.x=e(u),h=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(l=t,v=Ol[!l<<1|!s],n):l},n.y=function(t){return arguments.length?(s=t,v=Ol[!l<<1|!s],n):s},n.clamp=function(t){return arguments.length?(l&&s?(g=!!t[0],p=!!t[1]):l?g=!!t:s&&(p=!!t),n):l&&s?[g,p]:l?g:s?p:null},n.extent=function(t){var e,r,u,i,c;return arguments.length?(l&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),o=[e,r],l.invert&&(e=l(e),r=l(r)),e>r&&(c=e,e=r,r=c),(e!=f[0]||r!=f[1])&&(f=[e,r])),s&&(u=t[0],i=t[1],l&&(u=u[1],i=i[1]),a=[u,i],s.invert&&(u=s(u),i=s(i)),u>i&&(c=u,u=i,i=c),(u!=h[0]||i!=h[1])&&(h=[u,i])),n):(l&&(o?(e=o[0],r=o[1]):(e=f[0],r=f[1],l.invert&&(e=l.invert(e),r=l.invert(r)),e>r&&(c=e,e=r,r=c))),s&&(a?(u=a[0],i=a[1]):(u=h[0],i=h[1],s.invert&&(u=s.invert(u),i=s.invert(i)),u>i&&(c=u,u=i,i=c))),l&&s?[[e,u],[r,i]]:l?[e,r]:s&&[u,i])},n.clear=function(){return n.empty()||(f=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!l&&f[0]==f[1]||!!s&&h[0]==h[1]},ta.rebind(n,c,"on")};var Hl={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Ol=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Il=ac.format=gc.timeFormat,Yl=Il.utc,Zl=Yl("%Y-%m-%dT%H:%M:%S.%LZ");Il.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Jo:Zl,Jo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Jo.toString=Zl.toString,ac.second=Ft(function(n){return new cc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ac.seconds=ac.second.range,ac.seconds.utc=ac.second.utc.range,ac.minute=Ft(function(n){return new cc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ac.minutes=ac.minute.range,ac.minutes.utc=ac.minute.utc.range,ac.hour=Ft(function(n){var t=n.getTimezoneOffset()/60;return new cc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ac.hours=ac.hour.range,ac.hours.utc=ac.hour.utc.range,ac.month=Ft(function(n){return n=ac.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ac.months=ac.month.range,ac.months.utc=ac.month.utc.range;var Vl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Xl=[[ac.second,1],[ac.second,5],[ac.second,15],[ac.second,30],[ac.minute,1],[ac.minute,5],[ac.minute,15],[ac.minute,30],[ac.hour,1],[ac.hour,3],[ac.hour,6],[ac.hour,12],[ac.day,1],[ac.day,2],[ac.week,1],[ac.month,1],[ac.month,3],[ac.year,1]],$l=Il.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",Ne]]),Bl={range:function(n,t,e){return ta.range(Math.ceil(n/e)*e,+t,e).map(Ko)},floor:y,ceil:y};Xl.year=ac.year,ac.scale=function(){return Go(ta.scale.linear(),Xl,$l)};var Wl=Xl.map(function(n){return[n[0].utc,n[1]]}),Jl=Yl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",Ne]]);Wl.year=ac.year.utc,ac.scale.utc=function(){return Go(ta.scale.linear(),Wl,Jl)},ta.text=At(function(n){return n.responseText}),ta.json=function(n,t){return Nt(n,"application/json",Qo,t)},ta.html=function(n,t){return Nt(n,"text/html",na,t)},ta.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(ta):"object"==typeof module&&module.exports&&(module.exports=ta),this.d3=ta}();/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.4",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.4",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.4",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport),this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-mp.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.4",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.4",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.4",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a(document.body).height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl + * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT + * */ + +!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;bf&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+=""),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g"); +x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f); +var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&Cp&&C>1;){E=[],C--;for(var F=0;F(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,dM&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&Eg&&E>1;){F=[],E--;for(var G=0;G(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,dN&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D] +}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),k.pointActive(function(){return!1}),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},focusMargin:{get:function(){return x},set:function(a){x.top=void 0!==a.top?a.top:x.top,x.right=void 0!==a.right?a.right:x.right,x.bottom=void 0!==a.bottom?a.bottom:x.bottom,x.left=void 0!==a.left?a.left:x.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),h.pointActive(function(){return!1}),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},focusMargin:{get:function(){return r},set:function(a){r.top=void 0!==a.top?a.top:r.top,r.right=void 0!==a.right?a.right:r.right,r.bottom=void 0!==a.bottom?a.bottom:r.bottom,r.left=void 0!==a.left?a.left:r.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(G){return E.reset(),G.each(function(b){var G=k-j.left-j.right,H=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var I=0;if(z&&b.length&&(z=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),v){var J=d3.layout.stack().offset(w).values(function(a){return a.values}).y(r)(!b.length&&z?z:b);J.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=I++,J[c]=b[c]):c>0&&J[c-1].nonStackable&&J[c].values.map(function(a,b){a.y0-=J[c-1].values[b].y,a.y1=a.y0+a.y})}),b=J}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),v&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var K=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b,yErr:s(a,c)}})});m.domain(d||d3.merge(K).map(function(a){return a.x})).rangeBands(f||[0,G],C),n.domain(e||d3.extent(d3.merge(d3.merge(K).map(function(a){var c=a.y;v&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y);var d=a.yErr;return d?d.length?[c+d[0],c+d[1]]:(d=Math.abs(d),[c-d,c+d]):[c]})).concat(t))).range(g||[H,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var L=p.selectAll("g.nv-wrap.nv-multibar").data([b]),M=L.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),N=M.append("defs"),O=M.append("g"),P=L.select("g");O.append("g").attr("class","nv-groups"),L.attr("transform","translate("+j.left+","+j.top+")"),N.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),L.select("#nv-edge-clip-"+o+" rect").attr("width",G).attr("height",H),P.attr("clip-path",u?"url(#nv-edge-clip-"+o+")":"");var Q=L.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});Q.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var R=E.transition(Q.exit().selectAll("g.nv-bar"),"multibarExit",Math.min(100,B)).attr("y",function(a){var c=i(0)||0;return v&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();R.delay&&R.delay(function(a,b){var c=b*(B/(F+1))-b;return c}),Q.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return x(a,b)}).style("stroke",function(a,b){return x(a,b)}),Q.style("stroke-opacity",1).style("fill-opacity",.75);var S=Q.selectAll("g.nv-bar").data(function(a){return z&&!b.length?z.values:a.values});S.exit().remove();var T=S.enter().append("g").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,c,d){var e=v&&!b[d].nonStackable?0:d*m.rangeBand()/b.length,f=i(v&&!b[d].nonStackable?a.y0:0)||0;return"translate("+e+","+f+")"});T.append("rect").attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(v&&!b[d].nonStackable?1:b.length)}).style("fill",function(a,b,c){return x(a,c,b)}).style("stroke",function(a,b,c){return x(a,c,b)}),S.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0].values[0],0)&&(T.append("polyline"),S.select("polyline").attr("fill","none").attr("stroke",function(a,b,c){return y(a,c,b)}).attr("points",function(a,c){var d=s(a,c),e=.8*m.rangeBand()/(2*(v?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return n(a)-n(0)});var f=[[-e,d[0]],[e,d[0]],[0,d[0]],[0,d[1]],[-e,d[1]],[e,d[1]]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=m.rangeBand()/(2*(v?1:b.length)),e=r(a,c)<0?n(r(a,c))-n(0):0;return"translate("+d+", "+e+")"})),S.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),A&&(c||(c=b.map(function(){return!0})),S.select("rect").style("fill",function(a,b,d){return d3.rgb(A(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(A(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var U=S.watchTransition(E,"multibar",Math.min(250,B)).delay(function(a,c){return c*B/b[0].values.length});v?U.attr("transform",function(a,c,d){var e=0;e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1);var f=0;b[d].nonStackable&&(f=a.series*m.rangeBand()/b.length,b.length!==I&&(f=b[d].nonStackableSeries*m.rangeBand()/(2*I)));var g=f+m(q(a,c));return"translate("+g+","+e+")"}).select("rect").attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/I;return b.length!==I&&(e=m.rangeBand()/(2*I)),e}return m.rangeBand()}):U.attr("transform",function(a,c){var d=a.series*m.rangeBand()/b.length+m(q(a,c)),e=r(a,c)<0?n(0):n(0)-n(r(a,c))<1?n(0)-1:n(r(a,c))||0;return"translate("+d+","+e+")"}).select("rect").attr("width",m.rangeBand()/b.length).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(F=b[0].values.length)}),E.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=!0,v=!1,w="zero",x=a.utils.defaultColor(),y=a.utils.defaultColor(),z=!1,A=null,B=500,C=.1,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,B),F=0;return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return v},set:function(a){v=a}},stackOffset:{get:function(){return w},set:function(a){w=a}},clipEdge:{get:function(){return u},set:function(a){u=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return z +},set:function(a){z=a}},groupSpacing:{get:function(){return C},set:function(a){C=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return B},set:function(a){B=a,E.reset(B)}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}},barColor:{get:function(){return A},set:function(b){A=b?a.utils.getColor(b):null}},errorBarColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return F.reset(),m.each(function(b){var m=k-j.left-j.right,D=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),x&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),x&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var G=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1,yErr:s(a,b)}})});o.domain(d||d3.merge(G).map(function(a){return a.x})).rangeBands(f||[0,D],B),p.domain(e||d3.extent(d3.merge(d3.merge(G).map(function(a){var b=a.y;x&&(b=a.y>0?a.y1+a.y:a.y1);var c=a.yErr;return c?c.length?[b+c[0],b+c[1]]:(c=Math.abs(c),[b-c,b+c]):[b]})).concat(t))),p.range(y&&!x?g||[p.domain()[0]<0?A:0,m-(p.domain()[1]>0?A:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var H=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),I=H.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),J=(I.append("defs"),I.append("g"));H.select("g")}J.append("g").attr("class","nv-groups"),H.attr("transform","translate("+j.left+","+j.top+")");var K=H.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});K.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),K.exit().watchTransition(F,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),K.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),K.watchTransition(F,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var L=K.selectAll("g.nv-bar").data(function(a){return a.values});L.exit().remove();var M=L.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(x?a.y0:0)+","+(x?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});M.append("rect").attr("width",0).attr("height",o.rangeBand()/(x?1:b.length)),L.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),E.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),E.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){E.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){E.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){E.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){E.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0].values[0],0)&&(M.append("polyline"),L.select("polyline").attr("fill","none").attr("stroke",function(a,b,c){return w(a,c,b)}).attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(x?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(x?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),M.append("text"),y&&!x?(L.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=C(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+C(Math.abs(d[1]))+"-"+C(Math.abs(d[0])):c+"±"+C(Math.abs(d))}),L.watchTransition(F,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):L.selectAll("text").text(""),z&&!x?(M.append("text").classed("nv-bar-label",!0),L.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),L.watchTransition(F,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):L.selectAll("text.nv-bar-label").text(""),L.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),L.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),x?L.watchTransition(F,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):L.watchTransition(F,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),F.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=a.utils.defaultColor(),x=!1,y=!1,z=!1,A=60,B=.1,C=d3.format(",.2f"),D=250,E=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),F=a.utils.renderWatch(E,D);return b.dispatch=E,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return x},set:function(a){x=a}},showValues:{get:function(){return y},set:function(a){y=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return C},set:function(a){C=a}},valuePadding:{get:function(){return A},set:function(a){A=a}},groupSpacing:{get:function(){return B},set:function(a){B=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return D},set:function(a){D=a,F.reset(D)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}},errorBarColor:{get:function(){return w},set:function(b){w=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?B:A;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},D.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?B:A;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},D.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.seriesIndex].yAxis?B:A;a.point.x=x.x()(a.point),a.point.y=x.y()(a.point),D.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function E(a){var b=2===j[a.data.series].yAxis?B:A;a.value=v.x()(a.data),a.series={value:v.y()(a.data),color:a.color},D.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var F=d3.select(this);a.utils.initSVG(F),b.update=function(){F.transition().call(b)},b.container=this;var G=a.utils.availableWidth(g,F,e),H=a.utils.availableHeight(h,F,e),I=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),J=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),K=j.filter(function(a){return"scatter"==a.type&&1==a.yAxis}),L=j.filter(function(a){return"scatter"==a.type&&2==a.yAxis}),M=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),N=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),O=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),P=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,F),b;F.selectAll(".nv-noData").remove();var Q=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),R=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(Q.concat(R)),function(a){return a.x})).range([0,G]);var S=F.selectAll("g.wrap.multiChart").data([j]),T=S.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");T.append("g").attr("class","nv-x nv-axis"),T.append("g").attr("class","nv-y1 nv-axis"),T.append("g").attr("class","nv-y2 nv-axis"),T.append("g").attr("class","lines1Wrap"),T.append("g").attr("class","lines2Wrap"),T.append("g").attr("class","scatters1Wrap"),T.append("g").attr("class","scatters2Wrap"),T.append("g").attr("class","bars1Wrap"),T.append("g").attr("class","bars2Wrap"),T.append("g").attr("class","stack1Wrap"),T.append("g").attr("class","stack2Wrap"),T.append("g").attr("class","legendWrap");var U=S.select("g"),V=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var W=C.align()?G/2:G,X=C.align()?W:0;C.width(W),C.color(V),U.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(C),e.top!=C.height()&&(e.top=C.height(),H=a.utils.availableHeight(h,F,e)),U.select(".legendWrap").attr("transform","translate("+X+","+-e.top+")")}r.width(G).height(H).interpolate(m).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(G).height(H).interpolate(m).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"scatter"==j[b].type})),u.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"scatter"==j[b].type})),v.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),w.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),x.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),y.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),U.attr("transform","translate("+e.left+","+e.top+")");var Y=U.select(".lines1Wrap").datum(I.filter(function(a){return!a.disabled})),Z=U.select(".scatters1Wrap").datum(K.filter(function(a){return!a.disabled})),$=U.select(".bars1Wrap").datum(M.filter(function(a){return!a.disabled})),_=U.select(".stack1Wrap").datum(O.filter(function(a){return!a.disabled})),ab=U.select(".lines2Wrap").datum(J.filter(function(a){return!a.disabled})),bb=U.select(".scatters2Wrap").datum(L.filter(function(a){return!a.disabled})),cb=U.select(".bars2Wrap").datum(N.filter(function(a){return!a.disabled})),db=U.select(".stack2Wrap").datum(P.filter(function(a){return!a.disabled})),eb=O.length?O.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],fb=P.length?P.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(Q).concat(eb),function(a){return a.y})).range([0,H]),q.domain(d||d3.extent(d3.merge(R).concat(fb),function(a){return a.y})).range([0,H]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),x.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),y.yDomain(q.domain()),O.length&&d3.transition(_).call(x),P.length&&d3.transition(db).call(y),M.length&&d3.transition($).call(v),N.length&&d3.transition(cb).call(w),I.length&&d3.transition(Y).call(r),J.length&&d3.transition(ab).call(s),K.length&&d3.transition(Z).call(t),L.length&&d3.transition(bb).call(u),z._ticks(a.utils.calcTicksX(G/100,j)).tickSize(-H,0),U.select(".nv-x.nv-axis").attr("transform","translate(0,"+H+")"),d3.transition(U.select(".nv-x.nv-axis")).call(z),A._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-G,0),d3.transition(U.select(".nv-y1.nv-axis")).call(A),B._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-G,0),d3.transition(U.select(".nv-y2.nv-axis")).call(B),U.select(".nv-y1.nv-axis").classed("nv-disabled",Q.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),U.select(".nv-y2.nv-axis").classed("nv-disabled",R.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),C.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",l),u.dispatch.on("elementMouseover.tooltip",l),t.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),x.dispatch.on("elementMouseover.tooltip",n),y.dispatch.on("elementMouseover.tooltip",n),x.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),y.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",E),w.dispatch.on("elementMouseover.tooltip",E),v.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),v.dispatch.on("elementMousemove.tooltip",function(){D.position({top:d3.event.pageY,left:d3.event.pageX})()}),w.dispatch.on("elementMousemove.tooltip",function(){D.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.scatter().yScale(p),u=a.models.scatter().yScale(q),v=a.models.multiBar().stacked(!1).yScale(p),w=a.models.multiBar().stacked(!1).yScale(q),x=a.models.stackedArea().yScale(p),y=a.models.stackedArea().yScale(q),z=a.models.axis().scale(o).orient("bottom").tickPadding(5),A=a.models.axis().scale(p).orient("left"),B=a.models.axis().scale(q).orient("right"),C=a.models.legend().height(30),D=a.models.tooltip(),E=d3.dispatch();return b.dispatch=E,b.lines1=r,b.lines2=s,b.scatters1=t,b.scatters2=u,b.bars1=v,b.bars2=w,b.stack1=x,b.stack2=y,b.xAxis=z,b.yAxis1=A,b.yAxis2=B,b.tooltip=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return D.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),D.enabled(!!b)}},tooltipContent:{get:function(){return D.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),D.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a),x.x(a),y.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),t.y(a),u.y(a),x.y(a),y.y(a),v.y(a),w.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),x.useVoronoi(a),y.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b +}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;Mc)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+k+"-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+k+"-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var o=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){o(a,L.elementClick)}).on("dblclick",function(a){o(a,L.elementDblClick)}).on("mouseover",function(a){o(a,L.elementMouseover)}).on("mouseout",function(a){o(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b) +}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b)}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();/*! + +Holder - client side image placeholders +Version 2.7.1+6hydf +© 2015 Ivan Malopinsky - http://imsky.co + +Site: http://holderjs.com +Issues: https://github.com/imsky/holder/issues +License: http://opensource.org/licenses/MIT + +*/ +!function(a){if(a.document){var b=a.document;b.querySelectorAll||(b.querySelectorAll=function(c){var d,e=b.createElement("style"),f=[];for(b.documentElement.firstChild.appendChild(e),b._qsa=[],e.styleSheet.cssText=c+"{x-qsa:expression(document._qsa && document._qsa.push(this))}",a.scrollBy(0,0),e.parentNode.removeChild(e);b._qsa.length;)d=b._qsa.shift(),d.style.removeAttribute("x-qsa"),f.push(d);return b._qsa=null,f}),b.querySelector||(b.querySelector=function(a){var c=b.querySelectorAll(a);return c.length?c[0]:null}),b.getElementsByClassName||(b.getElementsByClassName=function(a){return a=String(a).replace(/^|\s+/g,"."),b.querySelectorAll(a)}),Object.keys||(Object.keys=function(a){if(a!==Object(a))throw TypeError("Object.keys called on non-object");var b,c=[];for(b in a)Object.prototype.hasOwnProperty.call(a,b)&&c.push(b);return c}),function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a.atob=a.atob||function(a){a=String(a);var c,d=0,e=[],f=0,g=0;if(a=a.replace(/\s/g,""),a.length%4===0&&(a=a.replace(/=+$/,"")),a.length%4===1)throw Error("InvalidCharacterError");if(/[^+/0-9A-Za-z]/.test(a))throw Error("InvalidCharacterError");for(;d>16&255)),e.push(String.fromCharCode(f>>8&255)),e.push(String.fromCharCode(255&f)),g=0,f=0),d+=1;return 12===g?(f>>=4,e.push(String.fromCharCode(255&f))):18===g&&(f>>=2,e.push(String.fromCharCode(f>>8&255)),e.push(String.fromCharCode(255&f))),e.join("")},a.btoa=a.btoa||function(a){a=String(a);var c,d,e,f,g,h,i,j=0,k=[];if(/[^\x00-\xFF]/.test(a))throw Error("InvalidCharacterError");for(;j>2,g=(3&c)<<4|d>>4,h=(15&d)<<2|e>>6,i=63&e,j===a.length+2?(h=64,i=64):j===a.length+1&&(i=64),k.push(b.charAt(f),b.charAt(g),b.charAt(h),b.charAt(i));return k.join("")}}(a),Object.prototype.hasOwnProperty||(Object.prototype.hasOwnProperty=function(a){var b=this.__proto__||this.constructor.prototype;return a in this&&(!(a in b)||b[a]!==this[a])}),function(){if("performance"in a==!1&&(a.performance={}),Date.now=Date.now||function(){return(new Date).getTime()},"now"in a.performance==!1){var b=Date.now();performance.timing&&performance.timing.navigationStart&&(b=performance.timing.navigationStart),a.performance.now=function(){return Date.now()-b}}}(),a.requestAnimationFrame||(a.webkitRequestAnimationFrame?!function(a){a.requestAnimationFrame=function(b){return webkitRequestAnimationFrame(function(){b(a.performance.now())})},a.cancelAnimationFrame=webkitCancelAnimationFrame}(a):a.mozRequestAnimationFrame?!function(a){a.requestAnimationFrame=function(b){return mozRequestAnimationFrame(function(){b(a.performance.now())})},a.cancelAnimationFrame=mozCancelAnimationFrame}(a):!function(a){a.requestAnimationFrame=function(b){return a.setTimeout(b,1e3/60)},a.cancelAnimationFrame=a.clearTimeout}(a))}}(this),function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):"object"==typeof exports?exports.Holder=b():a.Holder=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){(function(b){function d(a,b,c,d){var f=e(c.substr(c.lastIndexOf(a.domain)),a);f&&h({mode:null,el:d,flags:f,engineSettings:b})}function e(a,b){var c={theme:B(J.settings.themes.gray,null),stylesheets:b.stylesheets,instanceOptions:b};return a.match(/([\d]+p?)x([\d]+p?)(?:\?|$)/)?f(a,c):g(a,c)}function f(a,b){var c=a.split("?"),d=c[0].split("/");b.holderURL=a;var e=d[1],f=e.match(/([\d]+p?)x([\d]+p?)/);if(!f)return!1;if(b.fluid=-1!==e.indexOf("p"),b.dimensions={width:f[1].replace("p","%"),height:f[2].replace("p","%")},2===c.length){var g=A.parse(c[1]);if(g.bg&&(b.theme.background=(-1===g.bg.indexOf("#")?"#":"")+g.bg),g.fg&&(b.theme.foreground=(-1===g.fg.indexOf("#")?"#":"")+g.fg),g.theme&&b.instanceOptions.themes.hasOwnProperty(g.theme)&&(b.theme=B(b.instanceOptions.themes[g.theme],null)),g.text&&(b.text=g.text),g.textmode&&(b.textmode=g.textmode),g.size&&(b.size=g.size),g.font&&(b.font=g.font),g.align&&(b.align=g.align),b.nowrap=z.truthy(g.nowrap),b.auto=z.truthy(g.auto),z.truthy(g.random)){J.vars.cache.themeKeys=J.vars.cache.themeKeys||Object.keys(b.instanceOptions.themes);var h=J.vars.cache.themeKeys[0|Math.random()*J.vars.cache.themeKeys.length];b.theme=B(b.instanceOptions.themes[h],null)}}return b}function g(a,b){var c=!1,d=String.fromCharCode(11),e=a.replace(/([^\\])\//g,"$1"+d).split(d),f=/%[0-9a-f]{2}/gi,g=b.instanceOptions;b.holderURL=[];for(var h=e.length,i=0;h>i;i++){var j=e[i];if(j.match(f))try{j=decodeURIComponent(j)}catch(k){j=e[i]}var l=!1;if(J.flags.dimensions.match(j))c=!0,b.dimensions=J.flags.dimensions.output(j),l=!0;else if(J.flags.fluid.match(j))c=!0,b.dimensions=J.flags.fluid.output(j),b.fluid=!0,l=!0;else if(J.flags.textmode.match(j))b.textmode=J.flags.textmode.output(j),l=!0;else if(J.flags.colors.match(j)){var m=J.flags.colors.output(j);b.theme=B(b.theme,m),l=!0}else if(g.themes[j])g.themes.hasOwnProperty(j)&&(b.theme=B(g.themes[j],null)),l=!0;else if(J.flags.font.match(j))b.font=J.flags.font.output(j),l=!0;else if(J.flags.auto.match(j))b.auto=!0,l=!0;else if(J.flags.text.match(j))b.text=J.flags.text.output(j),l=!0;else if(J.flags.size.match(j))b.size=J.flags.size.output(j),l=!0;else if(J.flags.random.match(j)){null==J.vars.cache.themeKeys&&(J.vars.cache.themeKeys=Object.keys(g.themes));var n=J.vars.cache.themeKeys[0|Math.random()*J.vars.cache.themeKeys.length];b.theme=B(g.themes[n],null),l=!0}l&&b.holderURL.push(j)}return b.holderURL.unshift(g.domain),b.holderURL=b.holderURL.join("/"),c?b:!1}function h(a){var b=a.mode,c=a.el,d=a.flags,e=a.engineSettings,f=d.dimensions,g=d.theme,h=f.width+"x"+f.height;if(b=null==b?d.fluid?"fluid":"image":b,null!=d.text&&(g.text=d.text,"object"===c.nodeName.toLowerCase())){for(var j=g.text.split("\\n"),k=0;k1){var n,o=0,p=0,q=0;j=new e.Group("line"+q),("left"===a.align||"right"===a.align)&&(m=a.width*(1-2*(1-J.setup.lineWrapRatio)));for(var r=0;r=m||t===!0)&&(b(g,j,o,g.properties.leading),g.add(j),o=0,p+=g.properties.leading,q+=1,j=new e.Group("line"+q),j.y=p),t!==!0&&(i.moveTo(o,0),o+=h.spaceWidth+s.width,j.add(i))}if(b(g,j,o,g.properties.leading),g.add(j),"left"===a.align)g.moveTo(a.width-l,null,null);else if("right"===a.align){for(n in g.children)j=g.children[n],j.moveTo(a.width-j.width,null,null);g.moveTo(0-(a.width-l),null,null)}else{for(n in g.children)j=g.children[n],j.moveTo((g.width-j.width)/2,null,null);g.moveTo((a.width-g.width)/2,null,null)}g.moveTo(null,(a.height-g.height)/2,null),(a.height-g.height)/2<0&&g.moveTo(null,0,null)}else i=new e.Text(a.text),j=new e.Group("line0"),j.add(i),g.add(j),"left"===a.align?g.moveTo(a.width-l,null,null):"right"===a.align?g.moveTo(0-(a.width-l),null,null):g.moveTo((a.width-h.boundingBox.width)/2,null,null),g.moveTo(null,(a.height-h.boundingBox.height)/2,null);return d}function k(a,b,c){var d=parseInt(a,10),e=parseInt(b,10),f=Math.max(d,e),g=Math.min(d,e),h=.8*Math.min(g,f*J.defaults.scale);return Math.round(Math.max(c,h))}function l(a){var b;b=null==a||null==a.nodeType?J.vars.resizableImages:[a];for(var c=0,d=b.length;d>c;c++){var e=b[c];if(e.holderData){var f=e.holderData.flags,g=D(e);if(g){if(!e.holderData.resizeUpdate)continue;if(f.fluid&&f.auto){var h=e.holderData.fluidConfig;switch(h.mode){case"width":g.height=g.width/h.ratio;break;case"height":g.width=g.height*h.ratio}}var j={mode:"image",holderSettings:{dimensions:g,theme:f.theme,flags:f},el:e,engineSettings:e.holderData.engineSettings};"exact"==f.textmode&&(f.exactDimensions=g,j.holderSettings.dimensions=f.dimensions),i(j)}else p(e)}}}function m(a){if(a.holderData){var b=D(a);if(b){var c=a.holderData.flags,d={fluidHeight:"%"==c.dimensions.height.slice(-1),fluidWidth:"%"==c.dimensions.width.slice(-1),mode:null,initialDimensions:b};d.fluidWidth&&!d.fluidHeight?(d.mode="width",d.ratio=d.initialDimensions.width/parseFloat(c.dimensions.height)):!d.fluidWidth&&d.fluidHeight&&(d.mode="height",d.ratio=parseFloat(c.dimensions.width)/d.initialDimensions.height),a.holderData.fluidConfig=d}else p(a)}}function n(){for(var a,c=[],d=Object.keys(J.vars.invisibleImages),e=0,f=d.length;f>e;e++)a=J.vars.invisibleImages[d[e]],D(a)&&"img"==a.nodeName.toLowerCase()&&(c.push(a),delete J.vars.invisibleImages[d[e]]);c.length&&I.run({images:c}),b.requestAnimationFrame(n)}function o(){J.vars.visibilityCheckStarted||(b.requestAnimationFrame(n),J.vars.visibilityCheckStarted=!0)}function p(a){a.holderData.invisibleId||(J.vars.invisibleId+=1,J.vars.invisibleImages["i"+J.vars.invisibleId]=a,a.holderData.invisibleId=J.vars.invisibleId)}function q(a,b){return null==b?document.createElement(a):document.createElementNS(b,a)}function r(a,b){for(var c in b)a.setAttribute(c,b[c])}function s(a,b,c){var d,e;null==a?(a=q("svg",E),d=q("defs",E),e=q("style",E),r(e,{type:"text/css"}),d.appendChild(e),a.appendChild(d)):e=a.querySelector("style"),a.webkitMatchesSelector&&a.setAttribute("xmlns",E);for(var f=0;f=0;h--){var i=g.createProcessingInstruction("xml-stylesheet",'href="'+f[h]+'" rel="stylesheet"');g.insertBefore(i,g.firstChild)}g.removeChild(g.documentElement),e=d.serializeToString(g)}var j=d.serializeToString(a);return j=j.replace(/\&(\#[0-9]{2,}\;)/g,"&$1"),e+j}}function u(){return b.DOMParser?(new DOMParser).parseFromString("","application/xml"):void 0}function v(a){J.vars.debounceTimer||a.call(this),J.vars.debounceTimer&&b.clearTimeout(J.vars.debounceTimer),J.vars.debounceTimer=b.setTimeout(function(){J.vars.debounceTimer=null,a.call(this)},J.setup.debounce)}function w(){v(function(){l(null)})}var x=c(1),y=c(2),z=c(3),A=c(4),B=z.extend,C=z.getNodeArray,D=z.dimensionCheck,E="http://www.w3.org/2000/svg",F=8,G="2.7.1",H="\nCreated with Holder.js "+G+".\nLearn more at http://holderjs.com\n(c) 2012-2015 Ivan Malopinsky - http://imsky.co\n",I={version:G,addTheme:function(a,b){return null!=a&&null!=b&&(J.settings.themes[a]=b),delete J.vars.cache.themeKeys,this},addImage:function(a,b){var c=document.querySelectorAll(b);if(c.length)for(var d=0,e=c.length;e>d;d++){var f=q("img"),g={};g[J.vars.dataAttr]=a,r(f,g),c[d].appendChild(f)}return this},setResizeUpdate:function(a,b){a.holderData&&(a.holderData.resizeUpdate=!!b,a.holderData.resizeUpdate&&l(a))},run:function(a){a=a||{};var c={},f=B(J.settings,a);J.vars.preempted=!0,J.vars.dataAttr=f.dataAttr||J.vars.dataAttr,c.renderer=f.renderer?f.renderer:J.setup.renderer,-1===J.setup.renderers.join(",").indexOf(c.renderer)&&(c.renderer=J.setup.supportsSVG?"svg":J.setup.supportsCanvas?"canvas":"html");var g=C(f.images),i=C(f.bgnodes),j=C(f.stylenodes),k=C(f.objects);c.stylesheets=[],c.svgXMLStylesheet=!0,c.noFontFallback=f.noFontFallback?f.noFontFallback:!1;for(var l=0;l1){c.nodeValue="";for(var u=0;u=0?b:1)}function f(a){v?e(a):w.push(a)}null==document.readyState&&document.addEventListener&&(document.addEventListener("DOMContentLoaded",function y(){document.removeEventListener("DOMContentLoaded",y,!1),document.readyState="complete"},!1),document.readyState="loading");var g=a.document,h=g.documentElement,i="load",j=!1,k="on"+i,l="complete",m="readyState",n="attachEvent",o="detachEvent",p="addEventListener",q="DOMContentLoaded",r="onreadystatechange",s="removeEventListener",t=p in g,u=j,v=j,w=[];if(g[m]===l)e(b);else if(t)g[p](q,c,j),a[p](i,c,j);else{g[n](r,c),a[n](k,c);try{u=null==a.frameElement&&h}catch(x){}u&&u.doScroll&&!function z(){if(!v){try{u.doScroll("left")}catch(a){return e(z,50)}d(),b()}}()}return f.version="1.4.0",f.isReady=function(){return v},f}a.exports="undefined"!=typeof window&&b(window)},function(a,b,c){var d=c(5),e=function(a){function b(a,b){for(var c in b)a[c]=b[c];return a}var c=1,e=d.defclass({constructor:function(a){c++,this.parent=null,this.children={},this.id=c,this.name="n"+c,null!=a&&(this.name=a),this.x=0,this.y=0,this.z=0,this.width=0,this.height=0},resize:function(a,b){null!=a&&(this.width=a),null!=b&&(this.height=b)},moveTo:function(a,b,c){this.x=null!=a?a:this.x,this.y=null!=b?b:this.y,this.z=null!=c?c:this.z},add:function(a){var b=a.name;if(null!=this.children[b])throw"SceneGraph: child with that name already exists: "+b;this.children[b]=a,a.parent=this}}),f=d(e,function(b){this.constructor=function(){b.constructor.call(this,"root"),this.properties=a}}),g=d(e,function(a){function c(c,d){if(a.constructor.call(this,c),this.properties={fill:"#000"},null!=d)b(this.properties,d);else if(null!=c&&"string"!=typeof c)throw"SceneGraph: invalid node name"}this.Group=d.extend(this,{constructor:c,type:"group"}),this.Rect=d.extend(this,{constructor:c,type:"rect"}),this.Text=d.extend(this,{constructor:function(a){c.call(this),this.properties.text=a},type:"text"})}),h=new f;return this.Shape=g,this.root=h,this};a.exports=e},function(a,b){(function(a){b.extend=function(a,b){var c={};for(var d in a)a.hasOwnProperty(d)&&(c[d]=a[d]);if(null!=b)for(var e in b)b.hasOwnProperty(e)&&(c[e]=b[e]);return c},b.cssProps=function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c+":"+a[c]);return b.join(";")},b.encodeHtmlEntity=function(a){for(var b=[],c=0,d=a.length-1;d>=0;d--)c=a.charCodeAt(d),b.unshift(c>128?["&#",c,";"].join(""):a[d]);return b.join("")},b.getNodeArray=function(b){var c=null;return"string"==typeof b?c=document.querySelectorAll(b):a.NodeList&&b instanceof a.NodeList?c=b:a.Node&&b instanceof a.Node?c=[b]:a.HTMLCollection&&b instanceof a.HTMLCollection?c=b:b instanceof Array?c=b:null===b&&(c=[]),c},b.imageExists=function(a,b){var c=new Image;c.onerror=function(){b.call(this,!1)},c.onload=function(){b.call(this,!0)},c.src=a},b.decodeHtmlEntity=function(a){return a.replace(/&#(\d+);/g,function(a,b){return String.fromCharCode(b)})},b.dimensionCheck=function(a){var b={height:a.clientHeight,width:a.clientWidth};return b.height&&b.width?b:!1},b.truthy=function(a){return"string"==typeof a?"true"===a||"yes"===a||"1"===a||"on"===a||"✓"===a:!!a}}).call(b,function(){return this}())},function(a,b,c){var d=encodeURIComponent,e=decodeURIComponent,f=c(6),g=c(7),h=/(\w+)\[(\d+)\]/,i=/\w+\.\w+/;b.parse=function(a){if("string"!=typeof a)return{};if(a=f(a),""===a)return{};"?"===a.charAt(0)&&(a=a.slice(1));for(var b={},c=a.split("&"),d=0;da?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("