From f9ea115c19de7d52a7e34ab079a223c979147301 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 10:09:48 +0900 Subject: [PATCH 1/2] Fix some minor bugs in global functions and add unit tests --- common/functions.php | 9 +- tests/unit/functions/FunctionsTest.php | 152 +++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 tests/unit/functions/FunctionsTest.php diff --git a/common/functions.php b/common/functions.php index 154d60455..5f9214123 100644 --- a/common/functions.php +++ b/common/functions.php @@ -238,7 +238,7 @@ function starts_with($needle, $haystack, $case_sensitive = true) } else { - !strncasecmp($needle, $haystack, strlen($needle)); + return !strncasecmp($needle, $haystack, strlen($needle)); } } @@ -452,8 +452,11 @@ if (!function_exists('hex2bin')) */ function tobool($input) { - if (preg_match('/^(1|[ty].*|on|oui|si|vrai|aye)$/i', $input)) return true; - if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false; + if (is_scalar($input)) + { + if (preg_match('/^(1|[ty].*|on|ok.*oui|si|vrai|aye)$/i', $input)) return true; + if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false; + } return (bool)$input; } diff --git a/tests/unit/functions/FunctionsTest.php b/tests/unit/functions/FunctionsTest.php new file mode 100644 index 000000000..e89a73977 --- /dev/null +++ b/tests/unit/functions/FunctionsTest.php @@ -0,0 +1,152 @@ + 'xe', 'bar' => 'rhymix', 'key' => array('value1', 'value2', array('bar' => 'value3')), 'last' => 'bears'); + $flattened1 = array('foo' => 'xe', 'bar' => 'value3', 0 => 'value1', 1 => 'value2', 'last' => 'bears'); + $flattened2 = array(0 => 'xe', 1 => 'rhymix', 2 => 'value1', 3 => 'value2', 4 => 'value3', 5 => 'bears'); + + $this->assertEquals('foo', array_first_key($array)); + $this->assertEquals('xe', array_first($array)); + + $this->assertEquals('last', array_last_key($array)); + $this->assertEquals('bears', array_last($array)); + + $this->assertEquals($flattened1, array_flatten($array)); + $this->assertEquals($flattened2, array_flatten($array, false)); + } + + public function testClassBasename() + { + $this->assertEquals('FunctionsTest', class_basename($this)); + $this->assertEquals('FunctionsTest', class_basename(get_class($this))); + } + + public function testEscapeFunctions() + { + $this->assertEquals('<foo>&amp;</foo>', escape('&')); + $this->assertEquals('<foo>&</foo>', escape('&', false)); + + $this->assertEquals('expressionalertXSS', escape_css('expression:alert("XSS")')); + $this->assertEquals('#123456', escape_css('#123456')); + + $this->assertEquals('hello\\\\world', escape_js('hello\\world')); + $this->assertEquals('\u003Cbr \/\u003E', escape_js('
')); + + $this->assertEquals('hello\\\\world', escape_sqstr('hello\\world')); + $this->assertEquals('hello"world\\\'quotes', escape_sqstr('hello"world\'quotes')); + + $this->assertEquals('hello\\\\\\$world in \\"quotes\\"', escape_dqstr('hello\\$world in "quotes"')); + $this->assertEquals('\\${array[\'key\']}', escape_dqstr('${array[\'key\']}')); + } + + public function testExplodeWithEscape() + { + $this->assertEquals(array('foo', 'bar'), explode_with_escape(',', 'foo,bar')); + $this->assertEquals(array('foo', 'bar'), explode_with_escape(',', 'foo , bar')); + $this->assertEquals(array('foo', 'bar', 'baz,rhymix'), explode_with_escape(',', 'foo,bar,baz,rhymix', 3)); + $this->assertEquals(array('foo', 'bar', 'baz , rhymix'), explode_with_escape(',', 'foo,bar,baz , rhymix', 3)); + + $this->assertEquals(array('foo', 'bar,baz'), explode_with_escape(',', 'foo,bar\\,baz')); + $this->assertEquals(array('foo', 'bar\\', 'baz'), explode_with_escape(',', 'foo,bar\\ , baz')); + $this->assertEquals(array('foo', 'bar,baz', 'rhymix'), explode_with_escape(',', 'foo,bar\\,baz,rhymix')); + $this->assertEquals(array('foo', 'bar,baz'), explode_with_escape(',', 'foo,bar!,baz', null, '!')); + } + + public function testStartsEndsContains() + { + $this->assertTrue(starts_with('foo', 'foobar')); + $this->assertFalse(starts_with('FOO', 'foobar')); + $this->assertTrue(starts_with('FOO', 'foobar', false)); + $this->assertFalse(starts_with('bar', 'foobar')); + + $this->assertTrue(ends_with('bar', 'foobar')); + $this->assertFalse(ends_with('BAR', 'foobar')); + $this->assertTrue(ends_with('BAR', 'foobar', false)); + $this->assertFalse(ends_with('foo', 'foobar')); + + $this->assertTrue(contains('foo', 'foo bar baz rhymix rocks')); + $this->assertFalse(contains('barbaz', 'foo bar baz rhymix rocks')); + $this->assertTrue(contains('RHYMIX', 'foo bar baz rhymix rocks', false)); + $this->assertFalse(contains('ROCKS', 'foo bar baz rhymix rocks')); + } + + public function testRangeFunctions() + { + $this->assertTrue(is_between(5, 1, 10)); + $this->assertTrue(is_between(1, 1, 10)); + $this->assertTrue(is_between(10, 1, 10)); + $this->assertTrue(is_between(7, 1, 10, true)); + $this->assertFalse(is_between(1, 1, 10, true)); + $this->assertFalse(is_between(10, 1, 10, true)); + + $this->assertEquals(10, force_range(14, 1, 10)); + $this->assertEquals(3, force_range(3, 1, 10)); + $this->assertEquals(1, force_range(-4, 1, 10)); + } + + public function testUrlSafeBase64() + { + $this->assertEquals('Umh5bWl4IF5-', base64_encode_urlsafe('Rhymix ^~')); + $this->assertEquals('Rhymix ^~', base64_decode_urlsafe('Umh5bWl4IF5-')); + } + + public function testHex2Rgb2Hex() + { + $this->assertEquals(array(128, 128, 128), hex2rgb('808080')); + $this->assertEquals(array(60, 71, 244), hex2rgb('#3c47f4')); + $this->assertEquals(array(119, 119, 119), hex2rgb('#777')); + $this->assertEquals(array(51, 102, 153), hex2rgb('369')); + + $this->assertEquals('#808080', rgb2hex(array(128, 128, 128))); + $this->assertEquals('#3c47f4', rgb2hex(array(60, 71, 244))); + $this->assertEquals('777777', rgb2hex(array(119, 119, 119), false)); + $this->assertEquals('#000000', rgb2hex(array())); + } + + public function testToBool() + { + $this->assertTrue(tobool('Y')); + $this->assertTrue(tobool('yes')); + $this->assertTrue(tobool('on')); + $this->assertTrue(tobool('ok')); + $this->assertTrue(tobool('okay')); + $this->assertTrue(tobool('true')); + $this->assertTrue(tobool(1)); + $this->assertTrue(tobool(-1)); + $this->assertTrue(tobool(true)); + $this->assertTrue(tobool(array(1, 2, 3))); + + $this->assertFalse(tobool('N')); + $this->assertFalse(tobool('no')); + $this->assertFalse(tobool('false')); + $this->assertFalse(tobool('off')); + $this->assertFalse(tobool('Fuck you!')); + $this->assertFalse(tobool(0)); + $this->assertFalse(tobool('')); + $this->assertFalse(tobool(false)); + $this->assertFalse(tobool(null)); + $this->assertFalse(tobool(array())); + } + + public function testUTF8Functions() + { + $this->assertTrue(utf8_check('Hello, world!')); + $this->assertTrue(utf8_check('라이믹스')); + $this->assertTrue(utf8_check('')); + $this->assertTrue(utf8_check(iconv('UTF-8', 'EUC-KR', 'One CMS to rule them all...'))); + $this->assertFalse(utf8_check(iconv('UTF-8', 'EUC-KR', '라이믹스'))); + $this->assertFalse(utf8_check(chr(129) . chr(214) . chr(181) . chr(73) . chr(97))); + + $this->assertEquals('Emoticon: 😁', utf8_mbencode("Emoticon: \xf0\x9f\x98\x81")); + $this->assertEquals('Emoticon: 😜', utf8_mbencode("Emoticon: \xf0\x9f\x98\x9c")); + $this->assertEquals('한글은 인코딩하지 않음', utf8_mbencode('한글은 인코딩하지 않음')); + + $this->assertEquals("Weird spaces are in this string", utf8_normalize_spaces("Weird\x20spaces\xe2\x80\x80are\xe2\x80\x84in\xe2\x80\x86\xe2\x80\x8bthis\x0astring")); + $this->assertEquals("Weird spaces are in this\nstring", utf8_normalize_spaces("Weird\x20spaces\xe2\x80\x80are\xe2\x80\x84in\xe2\x80\x86\xe2\x80\x8bthis\x0astring", true)); + $this->assertEquals("Trimmed", utf8_trim("\x20\xe2\x80\x80Trimmed\xe2\x80\x84\xe2\x80\x86\xe2\x80\x8b")); + $this->assertEquals("Trimmed", utf8_trim("\x20\xe2\x80\x80Trimmed\x0a\x0c\x07\x09")); + } +} From d325ef99bc5494324066e00d81e8412a8b4646a3 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 11:36:36 +0900 Subject: [PATCH 2/2] Add unit tests for some legacy tests, too --- common/legacy.php | 2 +- tests/unit/framework/DateTimeTest.php | 17 +- tests/unit/functions/LegacyTest.php | 220 ++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 3 deletions(-) diff --git a/common/legacy.php b/common/legacy.php index 3f827fa96..3d4af5102 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -421,7 +421,7 @@ function getCurrentPageUrl($escape = true) */ function isSiteID($domain) { - return preg_match('/^([a-zA-Z0-9\_]+)$/', $domain); + return (bool)preg_match('/^([a-zA-Z0-9\_]+)$/', $domain); } /** diff --git a/tests/unit/framework/DateTimeTest.php b/tests/unit/framework/DateTimeTest.php index 737147bb3..d5adc1105 100644 --- a/tests/unit/framework/DateTimeTest.php +++ b/tests/unit/framework/DateTimeTest.php @@ -66,15 +66,28 @@ class DateTimeTest extends \Codeception\TestCase\Test { $timestamp = 1454000000; - // Test zdate() when the internal time zone is different from the default time zone. + // Test when the internal time zone is different from the default time zone. Rhymix\Framework\Config::set('locale.internal_timezone', 10800); $this->assertEquals('20160128195320', getInternalDateTime($timestamp)); - // Test zdate() when the internal time zone is the same as the default time zone. + // Test when the internal time zone is the same as the default time zone. Rhymix\Framework\Config::set('locale.internal_timezone', 32400); $this->assertEquals('20160129015320', getInternalDateTime($timestamp)); } + public function testGetDisplayDateTime() + { + $timestamp = 1454000000; + + // Test when the display time zone is different from the internal time zone. + $_SESSION['timezone'] = 'America/Los_Angeles'; + $this->assertEquals('20160128085320', getDisplayDateTime($timestamp)); + + // Test when the display time zone is the same as the internal time zone. + $_SESSION['timezone'] = 'Etc/GMT-3'; + $this->assertEquals('20160128195320', getDisplayDateTime($timestamp)); + } + public function testGetTimeGap() { $GLOBALS['lang'] = Rhymix\Framework\Lang::getInstance('en'); diff --git a/tests/unit/functions/LegacyTest.php b/tests/unit/functions/LegacyTest.php index 413b1f9cd..5bb693d56 100644 --- a/tests/unit/functions/LegacyTest.php +++ b/tests/unit/functions/LegacyTest.php @@ -2,5 +2,225 @@ class LegacyTest extends \Codeception\TestCase\Test { + public function testGetModule() + { + $this->assertTrue(getModule('board', 'controller') instanceof BoardController); + $this->assertTrue(getModule('board', 'model') instanceof BoardModel); + $this->assertTrue(getModule('board', 'view', 'admin') instanceof BoardAdminView); + $this->assertTrue(getModule('board') instanceof BoardView); + $this->assertTrue(getAdminController('board') instanceof BoardAdminController); + $this->assertTrue(getAdminModel('board') instanceof BoardAdminModel); + $this->assertTrue(getAdminView('board') instanceof BoardAdminView); + $this->assertTrue(getController('board') instanceof BoardController); + $this->assertTrue(getModel('board') instanceof BoardModel); + $this->assertTrue(getView('board') instanceof BoardView); + $this->assertTrue(getAPI('board') instanceof BoardApi); + $this->assertTrue(getMobile('board') instanceof BoardMobile); + $this->assertTrue(getWAP('board') instanceof BoardWap); + $this->assertTrue(getClass('board') instanceof Board); + } + public function testGetNextSequence() + { + if (!file_exists(\RX_BASEDIR . 'files/config/config.php')) + { + return; + } + + $this->assertGreaterThan(0, $sequence1 = getNextSequence()); + $this->assertGreaterThan($sequence1, $sequence2 = getNextSequence()); + + $this->assertTrue(checkUserSequence($sequence1)); + $this->assertTrue(checkUserSequence($sequence2)); + + $this->assertFalse(checkUserSequence(-1)); + setUserSequence(-1); + $this->assertTrue(checkUserSequence(-1)); + } + + public function testGetURL() + { + /** + * TODO: + * - getUrl() + * - getNotEncodedUrl() + * - getAutoEncodedUrl() + * - getFullUrl() + * - getNotEncodedFullUrl() + * - getSiteUrl() + * - getNotEncodedSiteUrl() + * - getFullSiteUrl() + * - getCurrentPageUrl() + * - getScriptPath() + * - getRequestUriByServerEnviroment() + */ + } + + public function testIsSiteID() + { + $this->assertTrue(isSiteID('rhymix_RHYMIX_1234')); + $this->assertFalse(isSiteID('www.rhymix.org')); + } + + public function testCutStr() + { + $this->assertEquals('안녕하세요? 라이믹스...', cut_str('안녕하세요? 라이믹스입니다. 제목이 너무 길어서 잘립니다.', 20)); + $this->assertEquals('Hello? This is Rhymix...', cut_str('Hello? This is Rhymix. This title is very long.', 20)); + } + + public function testTimeFunctions() + { + $this->assertEquals(0, get_time_zone_offset('00:00')); + $this->assertEquals(32400, get_time_zone_offset('+09:00')); + $this->assertEquals(32400, get_time_zone_offset('+0900')); + $this->assertEquals(32400, get_time_zone_offset('0900')); + $this->assertEquals(-18000, get_time_zone_offset('-05:00')); + $this->assertEquals(-18000, get_time_zone_offset('-0500')); + + $this->assertEquals('Jan', getMonthName(1)); + $this->assertEquals('Sep', getMonthName(9, true)); + $this->assertEquals('September', getMonthName(9, false)); + + /** + * The following functions are tested in DateTimeTest: + * - zgap() + * - zdate() + * - ztime() + * - getInternalDateTime() + * - getDisplayDateTime() + * - getTimeGap() + */ + } + + public function testGetEncodedEmailAddress() + { + $this->assertNotEquals('devops@rhymix.org', getEncodeEmailAddress('devops@rhymix.org')); + $this->assertContains('&#X', getEncodeEmailAddress('devops@rhymix.org')); + } + + public function testGetMicrotime() + { + $microtime1 = microtime(true); + $microtime2 = getMicroTime(); + $microtime3 = microtime(true); + + $this->assertEquals('double', gettype($microtime2)); + $this->assertGreaterThanOrEqual($microtime1, $microtime2); + $this->assertGreaterThanOrEqual($microtime2, $microtime3); + } + + public function testDelObjectVars() + { + $target = (object)array('foo' => 1, 'bar' => 2, 'baz' => 3, 'rhymix' => 4); + $delete = (object)array('bar' => 5, 'baz' => 6); + $result = delObjectVars($target, $delete); + + // Check if the keys were deleted from the result. + $this->assertTrue(isset($result->foo)); + $this->assertFalse(isset($result->bar)); + $this->assertFalse(isset($result->baz)); + $this->assertTrue(isset($result->rhymix)); + + // Check if the keys are intact in the original target. + $this->assertTrue(isset($target->bar)); + $this->assertTrue(isset($target->baz)); + } + + public function testGetDestroyXeVars() + { + // Test array. (Keys should be intact in the original target.) + $target = array('foo' => 1, 'bar' => 2, 'xe_validator_id' => 3); + $result = getDestroyXeVars($target); + $this->assertFalse(isset($result['xe_validator_id'])); + $this->assertTrue(isset($target['xe_validator_id'])); + + // Test object. (Keys should be deleted from the original target.) + $target = (object)array('foo' => 1, 'bar' => 2, 'xe_validator_id' => 3); + $result = getDestroyXeVars($target); + $this->assertFalse(isset($result->xe_validator_id)); + $this->assertFalse(isset($target->xe_validator_id)); + } + + public function testGetNumberingPath() + { + $this->assertEquals('001/', getNumberingPath(1)); + $this->assertEquals('012/', getNumberingPath(12)); + $this->assertEquals('123/', getNumberingPath(123)); + $this->assertEquals('234/001/', getNumberingPath(1234)); + $this->assertEquals('345/012/', getNumberingPath(12345)); + $this->assertEquals('456/123/', getNumberingPath(123456)); + $this->assertEquals('567/234/001/', getNumberingPath(1234567)); + $this->assertEquals('678/345/012/', getNumberingPath(12345678)); + $this->assertEquals('789/456/123/', getNumberingPath(123456789)); + } + + public function testMysqlPre4HashPassword() + { + $this->assertEquals('5d2e19393cc5ef67', mysql_pre4_hash_password('password')); + $this->assertEquals('25a4fb474e17c19a', mysql_pre4_hash_password('pass\'#word')); + } + + public function testJsonEncode2() + { + $data = array('foo' => 1, 'bar' => 2, 'baz' => 3, 'rhymix' => 4); + $this->assertEquals(json_encode($data), json_encode2($data)); + } + + public function TestIsCrawler() + { + $original_user_agent = $_SERVER['HTTP_USER_AGENT']; + + // Test automatic detection from User-Agent string. + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0'; + $this->assertFalse(isCrawler()); + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'; + $this->assertTrue(isCrawler()); + + // Test manual detection. + $this->assertTrue(isCrawler('Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)')); + $this->assertTrue(isCrawler('Yeti/1.0 (NHN Corp.; http://help.naver.com/robots/)')); + $this->assertFalse(isCrawler('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36')); + $this->assertFalse(isCrawler('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25')); + + $_SERVER['HTTP_USER_AGENT'] = $original_user_agent; + } + + public function testMiscUTF8Functions() + { + $this->assertEquals('<img>', url_decode('%3Cimg%3E')); + $this->assertEquals('한글 % English', utf8RawUrlDecode('%uD55C%uAE00%20%25%20English')); + $this->assertEquals('뷁', _code2utf(48577)); + + $this->assertTrue(detectUTF8('라이믹스')); + $this->assertTrue(detectUTF8(urlencode('라이믹스'))); + $this->assertTrue(detectUTF8('%87%a9%43%cd%ef', false, false)); + $this->assertFalse(detectUTF8(iconv('UTF-8', 'EUC-KR', '라이믹스'))); + $this->assertFalse(detectUTF8(chr(129) . chr(214) . chr(181) . chr(73) . chr(97))); + $this->assertFalse(detectUTF8('%87%a9%43%cd%ef')); + $this->assertEquals(mb_convert_encoding('라이믹스', 'UTF-8', 'CP949'), detectUTF8('라이믹스', true)); + $this->assertEquals('라이믹스', detectUTF8(iconv('UTF-8', 'EUC-KR', '라이믹스'), true)); + } + + public function testMiscSecurityFunctions() + { + /** + * TODO: + * - stripEmbedTagForAdmin() + * - checkCSRF() + */ + } + + public function testRecurciveExposureCheck() + { + /** + * TODO + */ + } + + public function testChangeValueInUrl() + { + /** + * TODO + */ + } }