From 9646fd3f3e3d68b3a2fd955710cee82159bd09db Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Fri, 14 Mar 2014 20:36:09 +0100 Subject: [PATCH] ADDED : Unit Tests --- Classes/PHPWord/Shared/File.php | 22 +-- Classes/PHPWord/Shared/String.php | 159 --------------------- Tests/PHPWord/DocumentPropertiesTest.php | 169 +++++++++++++++++++++++ Tests/PHPWord/Shared/FileTest.php | 40 ++++-- Tests/PHPWord/Shared/StringTest.php | 40 ++---- Tests/_files/documents/reader.docx.zip | Bin 0 -> 14208 bytes 6 files changed, 212 insertions(+), 218 deletions(-) create mode 100644 Tests/PHPWord/DocumentPropertiesTest.php create mode 100644 Tests/_files/documents/reader.docx.zip diff --git a/Classes/PHPWord/Shared/File.php b/Classes/PHPWord/Shared/File.php index 84cd2e84..3e3e147d 100755 --- a/Classes/PHPWord/Shared/File.php +++ b/Classes/PHPWord/Shared/File.php @@ -38,26 +38,8 @@ class PHPWord_Shared_File */ public static function file_exists($pFilename) { - // Sick construction, but it seems that - // file_exists returns strange values when - // doing the original file_exists on ZIP archives... - if (strtolower(substr($pFilename, 0, 3)) == 'zip') { - // Open ZIP file and verify if the file exists - $zipFile = substr($pFilename, 6, strpos($pFilename, '#') - 6); - $archiveFile = substr($pFilename, strpos($pFilename, '#') + 1); - - $zip = new ZipArchive(); - if ($zip->open($zipFile) === true) { - $returnValue = ($zip->getFromName($archiveFile) !== false); - $zip->close(); - return $returnValue; - } else { - return false; - } - } else { - // Regular file_exists - return file_exists($pFilename); - } + // Regular file_exists + return file_exists($pFilename); } /** diff --git a/Classes/PHPWord/Shared/String.php b/Classes/PHPWord/Shared/String.php index 2aa68d3f..64abdf1d 100755 --- a/Classes/PHPWord/Shared/String.php +++ b/Classes/PHPWord/Shared/String.php @@ -37,20 +37,6 @@ class PHPWord_Shared_String */ private static $_controlCharacters = array(); - /** - * Is mbstring extension avalable? - * - * @var boolean - */ - private static $_isMbstringEnabled; - - /** - * Is iconv extension avalable? - * - * @var boolean - */ - private static $_isIconvEnabled; - /** * Build control characters array */ @@ -65,40 +51,6 @@ class PHPWord_Shared_String } } - /** - * Get whether mbstring extension is available - * - * @return boolean - */ - public static function getIsMbstringEnabled() - { - if (isset(self::$_isMbstringEnabled)) { - return self::$_isMbstringEnabled; - } - - self::$_isMbstringEnabled = function_exists('mb_convert_encoding') ? - true : false; - - return self::$_isMbstringEnabled; - } - - /** - * Get whether iconv extension is available - * - * @return boolean - */ - public static function getIsIconvEnabled() - { - if (isset(self::$_isIconvEnabled)) { - return self::$_isIconvEnabled; - } - - self::$_isIconvEnabled = function_exists('iconv') ? - true : false; - - return self::$_isIconvEnabled; - } - /** * Convert from OpenXML escaped control character to PHP control character * @@ -155,115 +107,4 @@ class PHPWord_Shared_String { return $value === '' || preg_match('/^./su', $value) === 1; } - - /** - * Formats a numeric value as a string for output in various output writers - * - * @param mixed $value - * @return string - */ - public static function FormatNumber($value) - { - return number_format($value, 2, '.', ''); - } - - /** - * Converts a UTF-8 string into BIFF8 Unicode string data (8-bit string length) - * Writes the string using uncompressed notation, no rich text, no Asian phonetics - * If mbstring extension is not available, ASCII is assumed, and compressed notation is used - * although this will give wrong results for non-ASCII strings - * see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3 - * - * @param string $value UTF-8 encoded string - * @return string - */ - public static function UTF8toBIFF8UnicodeShort($value) - { - // character count - $ln = self::CountCharacters($value, 'UTF-8'); - - // option flags - $opt = (self::getIsMbstringEnabled() || self::getIsIconvEnabled()) ? - 0x0001 : 0x0000; - - // characters - $chars = self::ConvertEncoding($value, 'UTF-16LE', 'UTF-8'); - - $data = pack('CC', $ln, $opt) . $chars; - return $data; - } - - /** - * Converts a UTF-8 string into BIFF8 Unicode string data (16-bit string length) - * Writes the string using uncompressed notation, no rich text, no Asian phonetics - * If mbstring extension is not available, ASCII is assumed, and compressed notation is used - * although this will give wrong results for non-ASCII strings - * see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3 - * - * @param string $value UTF-8 encoded string - * @return string - */ - public static function UTF8toBIFF8UnicodeLong($value) - { - // character count - $ln = self::CountCharacters($value, 'UTF-8'); - - // option flags - $opt = (self::getIsMbstringEnabled() || self::getIsIconvEnabled()) ? - 0x0001 : 0x0000; - - // characters - $chars = self::ConvertEncoding($value, 'UTF-16LE', 'UTF-8'); - - $data = pack('vC', $ln, $opt) . $chars; - return $data; - } - - /** - * Convert string from one encoding to another. First try mbstring, then iconv, or no convertion - * - * @param string $value - * @param string $to Encoding to convert to, e.g. 'UTF-8' - * @param string $from Encoding to convert from, e.g. 'UTF-16LE' - * @return string - */ - public static function ConvertEncoding($value, $to, $from) - { - if (self::getIsMbstringEnabled()) { - $value = mb_convert_encoding($value, $to, $from); - return $value; - } - - if (self::getIsIconvEnabled()) { - $value = iconv($from, $to, $value); - return $value; - } - - // else, no conversion - return $value; - } - - /** - * Get character count. First try mbstring, then iconv, finally strlen - * - * @param string $value - * @param string $enc Encoding - * @return int Character count - */ - public static function CountCharacters($value, $enc = 'UTF-8') - { - if (self::getIsMbstringEnabled()) { - $count = mb_strlen($value, $enc); - return $count; - } - - if (self::getIsIconvEnabled()) { - $count = iconv_strlen($value, $enc); - return $count; - } - - // else strlen - $count = strlen($value); - return $count; - } } diff --git a/Tests/PHPWord/DocumentPropertiesTest.php b/Tests/PHPWord/DocumentPropertiesTest.php new file mode 100644 index 00000000..421f3269 --- /dev/null +++ b/Tests/PHPWord/DocumentPropertiesTest.php @@ -0,0 +1,169 @@ +setCreator(); + $this->assertEquals('', $oProperties->getCreator()); + + $oProperties->setCreator('AAA'); + $this->assertEquals('AAA', $oProperties->getCreator()); + } + + public function testLastModifiedBy() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setLastModifiedBy(); + $this->assertEquals('', $oProperties->getLastModifiedBy()); + + $oProperties->setLastModifiedBy('AAA'); + $this->assertEquals('AAA', $oProperties->getLastModifiedBy()); + } + + public function testCreated() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setCreated(); + $this->assertEquals(time(), $oProperties->getCreated()); + + $iTime = time() + 3600; + $oProperties->setCreated($iTime); + $this->assertEquals($iTime, $oProperties->getCreated()); + } + + public function testModified() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setModified(); + $this->assertEquals(time(), $oProperties->getModified()); + + $iTime = time() + 3600; + $oProperties->setModified($iTime); + $this->assertEquals($iTime, $oProperties->getModified()); + } + + public function testTitle() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setTitle(); + $this->assertEquals('', $oProperties->getTitle()); + + $oProperties->setTitle('AAA'); + $this->assertEquals('AAA', $oProperties->getTitle()); + } + + public function testDescription() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setDescription(); + $this->assertEquals('', $oProperties->getDescription()); + + $oProperties->setDescription('AAA'); + $this->assertEquals('AAA', $oProperties->getDescription()); + } + + public function testSubject() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setSubject(); + $this->assertEquals('', $oProperties->getSubject()); + + $oProperties->setSubject('AAA'); + $this->assertEquals('AAA', $oProperties->getSubject()); + } + + public function testKeywords() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setKeywords(); + $this->assertEquals('', $oProperties->getKeywords()); + + $oProperties->setKeywords('AAA'); + $this->assertEquals('AAA', $oProperties->getKeywords()); + } + + public function testCategory() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setCategory(); + $this->assertEquals('', $oProperties->getCategory()); + + $oProperties->setCategory('AAA'); + $this->assertEquals('AAA', $oProperties->getCategory()); + } + + public function testCompany() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setCompany(); + $this->assertEquals('', $oProperties->getCompany()); + + $oProperties->setCompany('AAA'); + $this->assertEquals('AAA', $oProperties->getCompany()); + } + + public function testManager() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setManager(); + $this->assertEquals('', $oProperties->getManager()); + + $oProperties->setManager('AAA'); + $this->assertEquals('AAA', $oProperties->getManager()); + } + + public function testCustomProperty() + { + $oProperties = new PHPWord_DocumentProperties(); + $oProperties->setCustomProperty('key1', null); + $oProperties->setCustomProperty('key2', true); + $oProperties->setCustomProperty('key3', 3); + $oProperties->setCustomProperty('key4', 4.4); + $oProperties->setCustomProperty('key5', 'value5'); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_STRING, $oProperties->getCustomPropertyType('key1')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_BOOLEAN, $oProperties->getCustomPropertyType('key2')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_INTEGER, $oProperties->getCustomPropertyType('key3')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_FLOAT, $oProperties->getCustomPropertyType('key4')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_STRING, $oProperties->getCustomPropertyType('key5')); + $this->assertEquals(null, $oProperties->getCustomPropertyType('key6')); + $this->assertEquals(null, $oProperties->getCustomPropertyValue('key1')); + $this->assertEquals(true, $oProperties->getCustomPropertyValue('key2')); + $this->assertEquals(3, $oProperties->getCustomPropertyValue('key3')); + $this->assertEquals(4.4, $oProperties->getCustomPropertyValue('key4')); + $this->assertEquals('value5', $oProperties->getCustomPropertyValue('key5')); + $this->assertEquals(null, $oProperties->getCustomPropertyValue('key6')); + $this->assertEquals(true, $oProperties->isCustomPropertySet('key5')); + $this->assertEquals(false, $oProperties->isCustomPropertySet('key6')); + $this->assertEquals(array('key1', 'key2', 'key3', 'key4', 'key5'), $oProperties->getCustomProperties()); + } + + public function testConvertProperty() + { + $this->assertEquals('', PHPWord_DocumentProperties::convertProperty('a', 'empty')); + $this->assertEquals(null, PHPWord_DocumentProperties::convertProperty('a', 'null')); + $this->assertEquals(8, PHPWord_DocumentProperties::convertProperty('8', 'int')); + $this->assertEquals(8, PHPWord_DocumentProperties::convertProperty('8.3', 'uint')); + $this->assertEquals(8.3, PHPWord_DocumentProperties::convertProperty('8.3', 'decimal')); + $this->assertEquals('8.3', PHPWord_DocumentProperties::convertProperty('8.3', 'lpstr')); + $this->assertEquals(strtotime('10/11/2013'), PHPWord_DocumentProperties::convertProperty('10/11/2013', 'date')); + $this->assertEquals(true, PHPWord_DocumentProperties::convertProperty('true', 'bool')); + $this->assertEquals(false, PHPWord_DocumentProperties::convertProperty('1', 'bool')); + $this->assertEquals('1', PHPWord_DocumentProperties::convertProperty('1', 'array')); + $this->assertEquals('1', PHPWord_DocumentProperties::convertProperty('1', '')); + + + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_INTEGER, PHPWord_DocumentProperties::convertPropertyType('int')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_INTEGER, PHPWord_DocumentProperties::convertPropertyType('uint')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_FLOAT, PHPWord_DocumentProperties::convertPropertyType('decimal')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_STRING, PHPWord_DocumentProperties::convertPropertyType('lpstr')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_DATE, PHPWord_DocumentProperties::convertPropertyType('date')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_BOOLEAN, PHPWord_DocumentProperties::convertPropertyType('bool')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_UNKNOWN, PHPWord_DocumentProperties::convertPropertyType('array')); + $this->assertEquals(PHPWord_DocumentProperties::PROPERTY_TYPE_UNKNOWN, PHPWord_DocumentProperties::convertPropertyType('')); + } +} diff --git a/Tests/PHPWord/Shared/FileTest.php b/Tests/PHPWord/Shared/FileTest.php index 89aa78cc..0f4df75e 100644 --- a/Tests/PHPWord/Shared/FileTest.php +++ b/Tests/PHPWord/Shared/FileTest.php @@ -16,12 +16,24 @@ class FileTest extends \PHPUnit_Framework_TestCase */ public function testFileExists() { - $dir = join( - DIRECTORY_SEPARATOR, - array(PHPWORD_TESTS_DIR_ROOT, '_files', 'templates') - ); - chdir($dir); - $this->assertTrue(PHPWord_Shared_File::file_exists('blank.docx')); + $dir = join( + DIRECTORY_SEPARATOR, + array(PHPWORD_TESTS_DIR_ROOT, '_files', 'templates') + ); + chdir($dir); + $this->assertTrue(PHPWord_Shared_File::file_exists('blank.docx')); + } + /** + * Test file_exists() + */ + public function testNoFileExists() + { + $dir = join( + DIRECTORY_SEPARATOR, + array(PHPWORD_TESTS_DIR_ROOT, '_files', 'templates') + ); + chdir($dir); + $this->assertFalse(PHPWord_Shared_File::file_exists('404.docx')); } /** @@ -29,13 +41,13 @@ class FileTest extends \PHPUnit_Framework_TestCase */ public function testRealpath() { - $dir = join( - DIRECTORY_SEPARATOR, - array(PHPWORD_TESTS_DIR_ROOT, '_files', 'templates') - ); - chdir($dir); - $file = 'blank.docx'; - $expected = $dir . DIRECTORY_SEPARATOR . $file; - $this->assertEquals($expected, PHPWord_Shared_File::realpath($file)); + $dir = join( + DIRECTORY_SEPARATOR, + array(PHPWORD_TESTS_DIR_ROOT, '_files', 'templates') + ); + chdir($dir); + $file = 'blank.docx'; + $expected = $dir . DIRECTORY_SEPARATOR . $file; + $this->assertEquals($expected, PHPWord_Shared_File::realpath($file)); } } diff --git a/Tests/PHPWord/Shared/StringTest.php b/Tests/PHPWord/Shared/StringTest.php index 41bd6d59..e04ebc23 100644 --- a/Tests/PHPWord/Shared/StringTest.php +++ b/Tests/PHPWord/Shared/StringTest.php @@ -11,32 +11,22 @@ use PHPWord_Shared_String; */ class StringTest extends \PHPUnit_Framework_TestCase { - /** - * Test getIsMbstringEnabled() and getIsIconvEnabled() - */ - public function testGetIsMbstringAndIconvEnabled() + public function testIsUTF8() { - $features = array( - 'mbstring' => 'mb_convert_encoding', - 'iconv' => 'iconv', - ); - foreach ($features as $key => $val) { - $expected = function_exists($val); - $get = "getIs{$key}Enabled"; - $firstResult = PHPWord_Shared_String::$get(); - $this->assertEquals($expected, $firstResult); - $secondResult = PHPWord_Shared_String::$get(); - $this->assertEquals($firstResult, $secondResult); - } + $this->assertTrue(PHPWord_Shared_String::IsUTF8('')); + $this->assertTrue(PHPWord_Shared_String::IsUTF8('éééé')); + $this->assertFalse(PHPWord_Shared_String::IsUTF8(utf8_decode('éééé'))); } - /** - * Test FormatNumber() - */ - public function testFormatNumber() - { - $expected = '1022.12'; - $returned = PHPWord_Shared_String::FormatNumber('1022.1234'); - $this->assertEquals($expected, $returned); - } + public function testControlCharacterOOXML2PHP() + { + $this->assertEquals('', PHPWord_Shared_String::ControlCharacterOOXML2PHP('')); + $this->assertEquals(chr(0x08), PHPWord_Shared_String::ControlCharacterOOXML2PHP('_x0008_')); + } + + public function testControlCharacterPHP2OOXML() + { + $this->assertEquals('', PHPWord_Shared_String::ControlCharacterPHP2OOXML('')); + $this->assertEquals('_x0008_', PHPWord_Shared_String::ControlCharacterPHP2OOXML(chr(0x08))); + } } diff --git a/Tests/_files/documents/reader.docx.zip b/Tests/_files/documents/reader.docx.zip new file mode 100644 index 0000000000000000000000000000000000000000..e2ceeb646e699713e15fb13b7f0da74be49688ee GIT binary patch literal 14208 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 l!vB59{44y9@vrcI4WM$8Z$LT<03d*VA|N#