TimeTrex/unit_tests/testcases/i18n/I18nTest.php

720 lines
40 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php /** @noinspection PhpMissingDocCommentInspection */
/*********************************************************************************
*
* TimeTrex is a Workforce Management program developed by
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
*
* You should have received a copy of the GNU Affero General Public License along
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
*
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
*
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by TimeTrex".
*
********************************************************************************/
/**
* @group i18nTest
*/
class i18nTest extends PHPUnit\Framework\TestCase {
function setUp(): void {
TTi18n::setLocale( 'en_CA' );
}
function tearDown(): void {
TTi18n::setLocale( 'en_CA' );
}
function testTryLocale() {
TTi18n::setLocale( 'en_US' );
$this->assertEquals( 'en_US', substr( TTi18n::getLocale(), 0, 5 ) );
$this->assertEquals( 'en', TTi18n::getLanguage() );
$this->assertEquals( 'US', TTi18n::getCountry() );
$this->assertEquals( '$', TTi18n::getCurrencySymbol( 'USD' ) );
$this->assertEquals( 'en_CA.UTF-8', TTi18n::tryLocale( 'en_CA.UTF-8' ) );
$this->assertEquals( 'en_US.utf-8', TTi18n::tryLocale( 'en_US.utf-8' ) );
$this->assertEquals( 'en_US.utf8', TTi18n::tryLocale( 'en_US.utf8' ) );
$this->assertEquals( 'en_US.uTf-8', TTi18n::tryLocale( 'en_US.uTf-8' ) );
$this->assertEquals( false, TTi18n::tryLocale( 'NOT REAL' ) );
//$this->assertEquals( TTi18n::tryLocale( 'en_CA' ), FALSE ); //Some installs may have this locale.
}
/**
* @noinspection PhpInconsistentReturnPointsInspection
*/
function testMisc() {
if ( extension_loaded( 'intl' ) == false ) {
return true;
}
//$expected_lang_arr = array('en' => 'English', 'da' => 'Danish (UO)', 'de' => 'German (UO)', 'es' => 'Spanish (UO)', 'id' => 'Indonesian (UO)', 'it' => 'Italian (UO)', 'fr' => 'French (UO)', 'pt' => 'Portuguese (UO)', 'ar' => 'Arabic (UO)', 'zh' => 'Chinese (UO)', 'yi' => 'Yiddish (UO)');
//$expected_lang_arr = array('en' => 'English', 'da' => 'Dansk (UO)', 'de' => 'Deutsch (UO)', 'es' => 'Español (UO)', 'id' => 'Bahasa Indonesia (UO)', 'it' => 'Italiano (UO)', 'fr' => 'Français (UO)', 'pt' => 'Português (UO)', 'ar' => 'العربية (UO)', 'zh' => '中文 (UO)', 'yi' => 'ייִדיש (UO)');
//$expected_lang_arr = array('en' => 'English', 'da' => 'Dansk (UO)', 'de' => 'Deutsch (UO)', 'es' => 'Español (UO)', 'id' => 'Indonesia (UO)', 'it' => 'Italiano (UO)', 'fr' => 'Français (UO)', 'pt' => 'Português (UO)', 'ar' => 'العربية (UO)', 'zh' => '中文 (UO)' );
$expected_lang_arr = [ 'en' => 'English', 'de' => 'Deutsch (UO)', 'es' => 'Español (UO)', 'id' => 'Indonesia (UO)', 'fr' => 'Français (UO)', 'hu' => 'Magyar (UO)', 'ro' => 'Română (UO)' ];
unset( $expected_lang_arr['id'] ); //It seems Indonesian changes depending on the Ubuntu version, so just ignore it.
TTi18n::setLocale( 'en_CA' );
$lang_arr = TTi18n::getLanguageArray();
unset( $lang_arr['id'] ); //It seems Indonesian changes depending on the Ubuntu version, so just ignore it.
array_pop( $lang_arr ); //Pop off Yiddish as it may not be installed everywhere.
$this->assertEquals( $lang_arr, $expected_lang_arr );
TTi18n::setLocale( 'es_ES' );
$lang_arr = TTi18n::getLanguageArray();
unset( $lang_arr['id'] ); //It seems Indonesian changes depending on the Ubuntu version, so just ignore it.
array_pop( $lang_arr ); //Pop off Yiddish as it may not be installed everywhere.
$this->assertEquals( $lang_arr, $expected_lang_arr );
TTi18n::setLocale( 'en_CA' );
$this->assertEquals( 'en_US', TTi18n::getNormalizedLocale() );
$this->assertEquals( false, TTi18n::getLocaleCookie() );
$this->assertEquals( 'en', TTi18n::getLanguageFromLocale() );
$this->assertEquals( 'fr', TTi18n::getLanguageFromLocale( 'fr_CH.utf8' ) );
}
/** @noinspection PhpInconsistentReturnPointsInspection */
function testFormatCurrency() {
if ( extension_loaded( 'intl' ) == false ) {
return true;
}
//Canadian Dollars - INTL wants to us CA$ rather than just $.
TTi18n::setLocale( 'en_CA' );
$this->assertEquals( '$8.99', TTi18n::formatCurrency( 8.9901 ) );
$this->assertEquals( '-$87.99', TTi18n::formatCurrency( -87.9901 ) );
$this->assertEquals( '¥8,799', TTi18n::formatCurrency( 8799.012345, 'JPY', 0 ) );
//US Dollars
TTi18n::setLocale( 'en_US' );
$this->assertEquals( '$8.99', TTi18n::formatCurrency( 8.9901 ) );
$this->assertEquals( '-$87.99', TTi18n::formatCurrency( -87.9901 ) );
$this->assertEquals( '¥8,799', TTi18n::formatCurrency( 8799.012345, 'JPY', 0 ) );
//test euros
TTi18n::setLocale( 'it_IT' );
if ( TTi18n::getThousandsSymbol() == '.' && TTi18n::getDecimalSymbol() == ',' ) {
$this->assertEquals( '8,99 €', TTi18n::formatCurrency( 8.9901 ) );
$this->assertEquals( '-87,99 €', TTi18n::formatCurrency( -87.9901 ) );
$this->assertEquals( '1.888.799,01 €', TTi18n::formatCurrency( 1888799.012345 ) );
$this->assertEquals( 'EUR 8.799,01 €', TTi18n::formatCurrency( 8799.012345, 'EUR', 1 ) );
$this->assertEquals( '8.799,01 €', TTi18n::formatCurrency( 8799.012345, 'EUR', 0 ) );
} else {
Debug::Text( 'ERROR: Locale differs, skipping unit tests...', __FILE__, __LINE__, __METHOD__, 1 );
}
TTi18n::setLocale( 'es_ES' );
if ( TTi18n::getThousandsSymbol() == '.' && TTi18n::getDecimalSymbol() == ',' ) {
//NBS = Non-Blocking Space. This requires PDF's (ie: Pay Stubs) to use UTF-8 encoding, even if they are English language. So we are removing NBS to avoid that.
//$this->assertEquals( '8,99 €', TTi18n::formatCurrency( 8.9901 ) );
$this->assertEquals( '8,99 €', TTi18n::formatCurrency( 8.9901 ) );
$this->assertEquals( '-87,99 €', TTi18n::formatCurrency( -87.9901 ) );
$this->assertEquals( '1.888.799,01 €', TTi18n::formatCurrency( 1888799.012345 ) );
$this->assertEquals( 'EUR 8.799,01 €', TTi18n::formatCurrency( 8799.012345, 'EUR', 1 ) );
$this->assertEquals( '8.799,01 €', TTi18n::formatCurrency( 8799.012345, 'EUR', 0 ) );
} else {
Debug::Text( 'ERROR: Locale differs, skipping unit tests...', __FILE__, __LINE__, __METHOD__, 1 );
}
}
function testNumberFormat() {
//english locales use #,###.##
TTi18n::setLocale( 'en_CA' );
$this->assertEquals( '8.99', TTi18n::formatNumber( 8.9901 ) );
$this->assertEquals( '-87.99', TTi18n::formatNumber( -87.9901 ) );
$this->assertEquals( '-88', TTi18n::formatNumber( -87.9991 ) );
$this->assertEquals( '8,987.99', TTi18n::formatNumber( 8987.990122 ) );
$this->assertEquals( '1,234.99', TTi18n::formatNumber( 1234.990122 ) );
$this->assertEquals( '123,456.99', TTi18n::formatNumber( 123456.990122 ) );
$this->assertEquals( '123,456,789.99', TTi18n::formatNumber( 123456789.990122 ) );
$this->assertEquals( '12,345.12', TTi18n::formatNumber( 12345.12345 ) );
$this->assertEquals( '-12,345.12', TTi18n::formatNumber( -12345.12345 ) );
$this->assertEquals( '123.12', TTi18n::formatNumber( 123.12345 ) );
$this->assertEquals( '-123.12', TTi18n::formatNumber( -123.12345 ) );
$this->assertEquals( '-87.990122', TTi18n::formatNumber( -87.990122, true, 2, 6 ) );
$this->assertEquals( '-87.90', TTi18n::formatNumber( -87.9000, true, 2, 4 ) );
$this->assertEquals( '-87.9901', TTi18n::formatNumber( -87.9901, true, 2, 4 ) );
$this->assertEquals( '1,234.9901', TTi18n::formatNumber( 1234.990122, true, 2, 4 ) );
$this->assertEquals( '123,456.99012', TTi18n::formatNumber( 123456.990122, true, 3, 5 ) );
$this->assertEquals( '123,456,789.9901', TTi18n::formatNumber( 123456789.990122, true, 2, 4 ) );
//spanish locales show numbers as # ###,##
TTi18n::setLocale( 'es_ES' );
if ( TTi18n::getThousandsSymbol() == '.' && TTi18n::getDecimalSymbol() == ',' ) {
$this->assertEquals( '8,99', TTi18n::formatNumber( 8.9901 ) );
$this->assertEquals( '-87,99', TTi18n::formatNumber( -87.9901 ) );
$this->assertEquals( '-87,99', TTi18n::formatNumber( -87.9901 ) );
$this->assertEquals( '8.987,99', TTi18n::formatNumber( 8987.990122 ) );
$this->assertEquals( '1.234,99', TTi18n::formatNumber( 1234.990122 ) );
$this->assertEquals( '123.456.789,99', TTi18n::formatNumber( 123456789.990122 ) );
$this->assertEquals( '-87,990156', TTi18n::formatNumber( -87.99015555, true, 2, 6 ) ); //rounding
$this->assertEquals( '-87,90', TTi18n::formatNumber( -87.9000, true, 2, 4 ) );
$this->assertEquals( '-87,9902', TTi18n::formatNumber( -87.990155, true, 2, 4 ) ); //rounding
$this->assertEquals( '1.234,9901', TTi18n::formatNumber( 1234.990122, true, 2, 4 ) );
$this->assertEquals( '123.456.789,9901', TTi18n::formatNumber( 123456789.990122, true, 2, 4 ) );
} else {
Debug::Text( 'ERROR: Locale differs, skipping unit tests...', __FILE__, __LINE__, __METHOD__, 1 );
}
//comparing TTi18n::formatNumber to Misc::MoneyFormat due to high usage of the MoneyFormat() in existing code.
TTi18n::setLocale( 'en_CA' );
$this->assertEquals( '12,345.15', TTi18n::formatNumber( 12345.152, true, 2, 2 ) );
$this->assertEquals( '12,345.15', TTi18n::formatNumber( 12345.151, false ) );
$this->assertEquals( '12,345.15', TTi18n::formatNumber( 12345.15, true ) );
$this->assertEquals( '12,345.15', TTi18n::formatNumber( 12345.15, false ) );
$this->assertEquals( '12,345.10', TTi18n::formatNumber( 12345.1, true ) );
$this->assertEquals( '12,345.5', TTi18n::formatNumber( 12345.5, false ) );
$this->assertEquals( '12,345.12', TTi18n::formatNumber( 12345.12345 ) );
$this->assertEquals( '-12,345.12', TTi18n::formatNumber( -12345.12345 ) );
$this->assertEquals( '123.12', TTi18n::formatNumber( 123.12345 ) );
$this->assertEquals( '-123.12', TTi18n::formatNumber( -123.12345 ) );
$this->assertEquals( '123', TTi18n::formatNumber( 123 ) );
$this->assertEquals( '-123', TTi18n::formatNumber( -123 ) );
$this->assertEquals( '12,345.15', Misc::MoneyFormat( 12345.152, true ) );
$this->assertEquals( '12345.15', Misc::MoneyFormat( 12345.151, false ) );
$this->assertEquals( '12,345.15', Misc::MoneyFormat( 12345.15, true ) );
$this->assertEquals( '12345.15', Misc::MoneyFormat( 12345.15, false ) );
$this->assertEquals( '12,345.10', Misc::MoneyFormat( 12345.1, true ) );
$this->assertEquals( '12345.50', Misc::MoneyFormat( 12345.5, false ) );
$this->assertEquals( '12,345.12', Misc::MoneyFormat( 12345.12345 ) );
$this->assertEquals( '-12,345.12', Misc::MoneyFormat( -12345.12345 ) );
$this->assertEquals( '123.12', Misc::MoneyFormat( 123.12345 ) );
$this->assertEquals( '-123.12', Misc::MoneyFormat( -123.12345 ) );
}
function testBeforeAndAfterDecimal() {
$this->assertEquals( '12345', Misc::getBeforeDecimal( 12345.92345 ) );
$this->assertEquals( '-12345', Misc::getBeforeDecimal( -12345.92345 ) );
$this->assertEquals( '92345', Misc::getAfterDecimal( 12345.92345, false ) );
$this->assertEquals( '92345', Misc::getAfterDecimal( -12345.92345, false ) );
TTi18n::setLocale( 'es_ES' );
$this->assertEquals( '12345', Misc::getBeforeDecimal( 12345.92345 ) );
$this->assertEquals( '-12345', Misc::getBeforeDecimal( -12345.92345 ) );
$this->assertEquals( '92345', Misc::getAfterDecimal( 12345.92345, false ) );
$this->assertEquals( '92345', Misc::getAfterDecimal( -12345.92345, false ) );
}
/*
* @noinspection PhpInconsistentReturnPointsInspection
*/
/** @noinspection PhpInconsistentReturnPointsInspection */
function testParseFloatFunctions() {
if ( extension_loaded( 'intl' ) == false ) {
return true;
}
TTi18n::setLocale( 'en_US' );
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1,234.123' ) );
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1, 234.123' ) );
$this->assertEquals( '12.91', TTI18n::parseFloat( '12.91' ) );
$this->assertEquals( '-12.91', TTI18n::parseFloat( '-12.91' ) );
$this->assertEquals( '12.123', TTI18n::parseFloat( (float)12.123 ) );
$this->assertEquals( '0.00', TTI18n::parseFloat( '0.00' ) );
$this->assertEquals( '0', TTI18n::parseFloat( '0' ) );
$this->assertEquals( '0', TTI18n::parseFloat( 0 ) );
$this->assertEquals( '0', TTI18n::parseFloat( '' ) );
$this->assertEquals( '0', TTI18n::parseFloat( true ) );
$this->assertEquals( '0', TTI18n::parseFloat( false ) );
$this->assertEquals( '0', TTI18n::parseFloat( null ) );
$this->assertEquals( '0', TTI18n::parseFloat( INF ) );
$this->assertEquals( '0', TTI18n::parseFloat( -INF ) );
$this->assertEquals( '0', TTI18n::parseFloat( +INF ) );
$this->assertEquals( '0', TTI18n::parseFloat( acos( 8 ) ) ); //acos(8) = NaN
//
//Test parsing both comma and decimal separated in a locale that uses just decimal separator
//
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1.234,123' ) );
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1. 234,123' ) );
$this->assertEquals( '12.91', TTI18n::parseFloat( '12,91' ) );
$this->assertEquals( '.12', TTI18n::parseFloat( '.12' ) );
$this->assertEquals( '.12', TTI18n::parseFloat( ',12' ) );
$this->assertEquals( '-.12', TTI18n::parseFloat( '-.12' ) );
$this->assertEquals( '-.12', TTI18n::parseFloat( '-,12' ) );
$this->assertEquals( '0.12', TTI18n::parseFloat( '0.12' ) );
$this->assertEquals( '0.12', TTI18n::parseFloat( '0,12' ) );
$this->assertEquals( '-0.12', TTI18n::parseFloat( '-0.12' ) );
$this->assertEquals( '-0.12', TTI18n::parseFloat( '-0,12' ) );
$this->assertEquals( '12.9', TTI18n::parseFloat( '12.9', 1 ) ); //Ambiguous as it could be assumed to be 12.9, or 129
$this->assertEquals( '12.91', TTI18n::parseFloat( '12.91', 2 ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91
$this->assertEquals( '12.912', TTI18n::parseFloat( '12.912', 3 ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12.9123', 4 ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123
$this->assertEquals( '12.9', TTI18n::parseFloat( '12,9', 1 ) ); //Ambiguous as it could be assumed to be 12.9, or 129
$this->assertEquals( '12.91', TTI18n::parseFloat( '12,91', 2 ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91
$this->assertEquals( '12.912', TTI18n::parseFloat( '12,912', 3 ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12,9123', 4 ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123
$this->assertEquals( '12.9', TTI18n::parseFloat( '12.9' ) ); //Ambiguous as it could be assumed to be 12.9, or 129 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.91', TTI18n::parseFloat( '12.91' ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.912', TTI18n::parseFloat( '12.912' ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12.9123' ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.9', TTI18n::parseFloat( '12,9' ) ); //Ambiguous as it could be assumed to be 12.9, or 129
$this->assertEquals( '12.91', TTI18n::parseFloat( '12,91' ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91
$this->assertEquals( '12912', TTI18n::parseFloat( '12,912' ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12,9123' ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123
$this->assertEquals( '123', TTI18n::parseFloat( '123' ) );
$this->assertEquals( '1234', TTI18n::parseFloat( '1, 234' ) ); //Ambiguous as it could be assumed to be 1,234, or 1.234.
$this->assertEquals( '1.234', TTI18n::parseFloat( '1. 234' ) ); //Ambiguous as it could be assumed to be 1,234, or 1.234 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '123.91', TTI18n::parseFloat( '123.91' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '123,91' ) );
$this->assertEquals( '1234.91', TTI18n::parseFloat( '1, 234.91' ) );
$this->assertEquals( '1234.91', TTI18n::parseFloat( '1. 234,91' ) );
$this->assertEquals( '123456789.91', TTI18n::parseFloat( '123 456 789.91' ) );
$this->assertEquals( '123456789.91', TTI18n::parseFloat( '123 456 789,91' ) );
$this->assertEquals( '123456789.91', TTI18n::parseFloat( '123, 456, 789.91' ) );
$this->assertEquals( '123456789.91', TTI18n::parseFloat( '123. 456. 789,91' ) );
$this->assertEquals( '123456789.912', TTI18n::parseFloat( '123. 456. 789,912' ) );
$this->assertEquals( '123456789.912345678', TTI18n::parseFloat( '123. 456. 789,912345678' ) );
$this->assertEquals( '1234567890123456789000.123', TTI18n::parseFloat( '1,234,567,890,123,456,789,000.123' ) );
$this->assertEquals( '1234567890123456789123.123', TTI18n::parseFloat( '1,234,567,890,123,456,789,123.123' ) );
$this->assertEquals( '0.00', TTI18n::parseFloat( '0,00' ) );
$this->assertEquals( '0', TTI18n::parseFloat( '0' ) );
$this->assertEquals( '0', TTI18n::parseFloat( 0 ) );
//Test floats with other bogus characters.
$this->assertEquals( '123.91', TTI18n::parseFloat( '$123.91ABC%' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '$123,91ABC%' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( 'A123.91' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( 'A123.91B' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '12A3.91' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '123A.91' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '123.A91' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '*&#$#\'"123.JKLFDJFL91%' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '$123.91-' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '$123.91--' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '$123.91+' ) );
$this->assertEquals( '123.91', TTI18n::parseFloat( '$123.91++' ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( '54333.12', 2 ) ); //Has only one separator, and input matches precision value.
$this->assertEquals( '54333.12', TTI18n::parseFloat( '54333.12', 3 ) ); //Has only one separator, and input is 2 decimal places which DOES NOT match precision value.
$this->assertEquals( '54333.123', TTI18n::parseFloat( '54333.123', 3 ) ); //Has only one separator, and input matches precision value.
$this->assertEquals( '54333.1234', TTI18n::parseFloat( '54333.1234', 4 ) ); //Has only one separator, and input matches precision value.
//Make sure parseFloat() can handle output from formatNumber()
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatNumber( '54333.1234' ) ) ); //Auto formatting decimals.
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatNumber( '54333.12' ) ) );
$this->assertEquals( '54333.123', TTI18n::parseFloat( TTi18n::formatNumber( '54333.123', true, 3, 3 ), 3 ) );
$this->assertEquals( '54333.1234', TTI18n::parseFloat( TTi18n::formatNumber( '54333.1234', true, 4, 4 ), 4 ) );
//Make sure parseFloat() can handle output from formatCurrency()
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234' ) ) ); //Auto formatting decimals.
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.12' ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.123' ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234' ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234', 'EUR', true ) ) ); //Auto formatting decimals.
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.12', 'EUR', true ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.123', 'EUR', true ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234', 'EUR', true ) ) );
TTi18n::setLocale( 'fr_CA' );
$this->assertEquals( '54333.12', TTI18n::parseFloat( '54333.12', 2 ) ); //Has only one separator, and input matches precision value.
$this->assertEquals( '54333.12', TTI18n::parseFloat( '54333.12', 3 ) ); //Has only one separator, and input is 2 decimal places which DOES NOT match precision value.
$this->assertEquals( '54333.123', TTI18n::parseFloat( '54333.123', 3 ) ); //Has only one separator, and input matches precision value.
$this->assertEquals( '54333.1234', TTI18n::parseFloat( '54333.1234', 4 ) ); //Has only one separator, and input matches precision value.
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1.234,123' ) ); //Has both separators, so can be parsed properly.
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1. 234,123' ) ); //Has both separators, so can be parsed properly.
$this->assertEquals( '1234.91', TTI18n::parseFloat( '1 234,91' ) );
$this->assertEquals( '12.91', TTI18n::parseFloat( '12,91' ) );
$this->assertEquals( '0.00', TTI18n::parseFloat( '0,00' ) );
$this->assertEquals( '12.123', TTI18n::parseFloat( (float)12.123 ) ); //If its input as an actual float value, it shouldn't be touched.
TTi18n::setLocale( 'es_ES' );
if ( TTi18n::getThousandsSymbol() == '.' && TTi18n::getDecimalSymbol() == ',' ) {
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1.234,123' ) );
$this->assertEquals( '1234.123', TTI18n::parseFloat( '1. 234,123' ) );
$this->assertEquals( '12.91', TTI18n::parseFloat( '12,91' ) );
$this->assertEquals( '0.00', TTI18n::parseFloat( '0,00' ) );
$this->assertEquals( '12.123', TTI18n::parseFloat( (float)12.123 ) ); //If its input as an actual float value, it shouldn't be touched.
$this->assertEquals( '12.9', TTI18n::parseFloat( '12.9', 1 ) ); //Ambiguous as it could be assumed to be 12.9, or 129
$this->assertEquals( '12.91', TTI18n::parseFloat( '12.91', 2 ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91
$this->assertEquals( '12.912', TTI18n::parseFloat( '12.912', 3 ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12.9123', 4 ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123
$this->assertEquals( '12.9', TTI18n::parseFloat( '12,9', 1 ) ); //Ambiguous as it could be assumed to be 12.9, or 129
$this->assertEquals( '12.91', TTI18n::parseFloat( '12,91', 2 ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91
$this->assertEquals( '12.912', TTI18n::parseFloat( '12,912', 3 ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12,9123', 4 ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123
$this->assertEquals( '12.9', TTI18n::parseFloat( '12,9' ) ); //Ambiguous as it could be assumed to be 12.9, or 129 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.91', TTI18n::parseFloat( '12,91' ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.912', TTI18n::parseFloat( '12,912' ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12,9123' ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123 -- However since there is only one separator and it matches the decimal separator in the locale we can be certain.
$this->assertEquals( '12.9', TTI18n::parseFloat( '12.9' ) ); //Ambiguous as it could be assumed to be 12.9, or 129
$this->assertEquals( '12.91', TTI18n::parseFloat( '12.91' ) ); //Ambiguous as it could be assumed to be 12.91, or 12, 91
$this->assertEquals( '12912', TTI18n::parseFloat( '12.912' ) ); //Ambiguous as it could be assumed to be 12.912, or 12, 912
$this->assertEquals( '12.9123', TTI18n::parseFloat( '12.9123' ) ); //Ambiguous as it could be assumed to be 12.9123, or 12, 9123
//Make sure parseFloat() can handle output from formatNumber()
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatNumber( '54333.1234' ) ) ); //Auto formatting decimals.
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatNumber( '54333.12' ) ) );
$this->assertEquals( '54333.123', TTI18n::parseFloat( TTi18n::formatNumber( '54333.123', true, 3, 3 ), 3 ) );
$this->assertEquals( '54333.1234', TTI18n::parseFloat( TTi18n::formatNumber( '54333.1234', true, 4, 4 ), 4 ) );
//Make sure parseFloat() can handle output from formatCurrency()
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234' ) ) ); //Auto formatting decimals.
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.12' ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.123' ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234' ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234', 'EUR', true ) ) ); //Auto formatting decimals.
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.12', 'EUR', true ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.123', 'EUR', true ) ) );
$this->assertEquals( '54333.12', TTI18n::parseFloat( TTi18n::formatCurrency( '54333.1234', 'EUR', true ) ) );
} else {
Debug::Text( 'ERROR: Locale differs, skipping unit tests...', __FILE__, __LINE__, __METHOD__, 1 );
}
}
/*
* @noinspection PhpInconsistentReturnPointsInspection
*/
function testUserWage() {
//
//Test end-to-end setting of user wage and viewing it in a report.
//
TTDate::setTimeZone( 'America/Vancouver', true ); //Due to being a singleton and PHPUnit resetting the state, always force the timezone to be set.
$dd = new DemoData();
$dd->setEnableQuickPunch( false ); //Helps prevent duplicate punch IDs and validation failures.
$dd->setUserNamePostFix( '_' . uniqid( null, true ) ); //Needs to be super random to prevent conflicts and random failing tests.
$company_id = $dd->createCompany();
$legal_entity_id = $dd->createLegalEntity( $company_id, 10 );
Debug::text( 'Company ID: ' . $company_id, __FILE__, __LINE__, __METHOD__, 10 );
$this->assertGreaterThan( 0, $company_id );
//Permissions are required so the user has permissions to run reports.
$dd->createPermissionGroups( $company_id, 40 ); //Administrator only.
$dd->createCurrency( $company_id, 10 );
$dd->createUserWageGroups( $company_id );
$user_id = $dd->createUser( $company_id, $legal_entity_id, 100 );
$user_idb = $dd->createUser( $company_id, $legal_entity_id, 10 );
//Delete all wages so we there is a clean slate and there are no random wages that might occur *after* the ones we create below, which would cause the report to be different.
// Skip first wage on the employees hire date.
$uwlf = TTnew('UserWageListFactory');
$uwlf->getByUserId( $user_id, [ 'effective_date' => 'asc', 'wage_group_id' => 'asc' ] );
if ( $uwlf->getRecordCount() > 0 ) {
$i = 0;
foreach( $uwlf as $uw_obj ) {
if ( $i == 0 ) { //Skip first wage effective date.
$i++;
continue;
}
$uw_obj->setDeleted( true );
$uw_obj->Save();
$i++;
}
}
$uwlf = TTnew('UserWageListFactory');
$uwlf->getByUserId( $user_idb, [ 'effective_date' => 'asc', 'wage_group_id' => 'asc' ] );
if ( $uwlf->getRecordCount() > 0 ) {
$i = 0;
foreach( $uwlf as $uw_obj ) {
if ( $i == 0 ) { //Skip first wage effective date.
$i++;
continue;
}
$uw_obj->setDeleted( true );
$uw_obj->Save();
$i++;
}
}
//Get User Object.
$ulf = new UserListFactory();
$user_obj = $ulf->getById( $user_id )->getCurrent();
$this->assertGreaterThan( 0, $user_id );
TTi18n::setLocale( 'en_US' );
$uw = new UserWageFactory();
$data = [
'user_id' => $user_id,
'type_id' => 20, //Salary Annual
'wage' => '54, 333.12',
'weekly_time' => ( 40 * 3600 ),
'hourly_rate' => '1, 123.98',
'labor_burden_percent' => '12.98%',
'effective_date' => strtotime( '01-Jan-2019' ),
];
$uw->setObjectFromArray( $data );
$insert_id = $uw->Save( false );
$uwlf = new UserWageListFactory();
$uwlf->getById( $insert_id );
$retarr = $uwlf->getCurrent()->getObjectAsArray();
//var_dump($retarr);
$this->assertEquals( '54333.12', $retarr['wage'] );
$this->assertEquals( '1123.98', $retarr['hourly_rate'] );
$this->assertEquals( '12.98', $retarr['labor_burden_percent'] );
TTi18n::setLocale( 'fr_CA' );
$uw = new UserWageFactory();
$data = [
'user_id' => $user_idb,
'type_id' => 20, //Salary Annual
'wage' => '54. 334,12',
'weekly_time' => ( 40 * 3600 ),
'hourly_rate' => '1. 124,98',
'labor_burden_percent' => '13,98%',
'effective_date' => strtotime( '01-Jan-2019' ),
];
$uw->setObjectFromArray( $data );
$insert_id = $uw->Save( false );
$uwlf = new UserWageListFactory();
$uwlf->getById( $insert_id );
$retarr = $uwlf->getCurrent()->getObjectAsArray();
//var_dump($retarr);
$this->assertEquals( '54334.12', $retarr['wage'] );
$this->assertEquals( '1124.98', $retarr['hourly_rate'] );
$this->assertEquals( '13.98', $retarr['labor_burden_percent'] );
//Generate Report in en_US
TTi18n::setLocale( 'en_US' );
$report_obj = new UserSummaryReport();
$report_obj->setUserObject( $user_obj );
$report_obj->setPermissionObject( new Permission() );
$report_config = Misc::trimSortPrefix( $report_obj->getTemplate( 'by_employee+wage' ) );
$report_config['columns'][] = 'labor_burden_percent';
$report_obj->setConfig( $report_config );
//var_dump($report_config);
$report_output = $report_obj->getOutput( 'raw' );
//var_dump($report_output);
$this->assertEquals( 54333.12, $report_output[0]['wage'] );
$this->assertEquals( 1123.98, $report_output[0]['hourly_rate'] );
$this->assertEquals( 12.98, $report_output[0]['labor_burden_percent'] );
$this->assertEquals( 54334.12, $report_output[1]['wage'] );
$this->assertEquals( 1124.98, $report_output[1]['hourly_rate'] );
$this->assertEquals( 13.98, $report_output[1]['labor_burden_percent'] );
//Generate Report in fr_CA
TTi18n::setLocale( 'fr_CA' );
$report_obj = new UserSummaryReport();
$report_obj->setUserObject( $user_obj );
$report_obj->setPermissionObject( new Permission() );
$report_config = Misc::trimSortPrefix( $report_obj->getTemplate( 'by_employee+wage' ) );
$report_config['columns'][] = 'labor_burden_percent';
$report_obj->setConfig( $report_config );
//var_dump($report_config);
$report_output = $report_obj->getOutput( 'raw' );
//var_dump($report_output);
$this->assertEquals( 54333.12, $report_output[0]['wage'] );
$this->assertEquals( 1123.98, $report_output[0]['hourly_rate'] );
$this->assertEquals( 12.98, $report_output[0]['labor_burden_percent'] );
$this->assertEquals( 54334.12, $report_output[1]['wage'] );
$this->assertEquals( 1124.98, $report_output[1]['hourly_rate'] );
$this->assertEquals( 13.98, $report_output[1]['labor_burden_percent'] );
}
function testRemoveTrailingZeros() {
TTi18n::setLocale( 'en_US' );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.45, 2 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.45, 0 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.450000, 2 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( '123.450000', 2 ), (float)123.45 );
$this->assertEquals( 'test', Misc::removeTrailingZeros( 'test', 2 ) ); //Make sure if it can't work with the input value, we just output it untouched.
TTi18n::setLocale( 'fr_CA' );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.45, 2 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.45, 0 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.450000, 2 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( '123.450000', 2 ), (float)123.45 );
$this->assertEquals( 'test', Misc::removeTrailingZeros( 'test', 2 ) ); //Make sure if it can't work with the input value, we just output it untouched.
TTi18n::setLocale( 'es_ES' );
if ( TTi18n::getThousandsSymbol() == '.' && TTi18n::getDecimalSymbol() == ',' ) {
$this->assertEquals( Misc::removeTrailingZeros( (float)123.45, 2 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.45, 0 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( (float)123.450000, 2 ), (float)123.45 );
$this->assertEquals( Misc::removeTrailingZeros( '123.450000', 2 ), (float)123.45 );
$this->assertEquals( 'test', Misc::removeTrailingZeros( 'test', 2 ) ); //Make sure if it can't work with the input value, we just output it untouched.
} else {
Debug::Text( 'ERROR: Locale differs, skipping unit tests...', __FILE__, __LINE__, __METHOD__, 1 );
}
}
function testSetLocale() {
$this->assertEquals( true, TTi18n::setLocale( 'en_GB' ) );
$this->assertEquals( 'en_GB.UTF-8', TTi18n::getLocale() );
$this->assertEquals( '€', TTi18n::getCurrencySymbol( 'EUR' ) );
$this->assertEquals( true, TTi18n::setLocale( 'en_CA' ) );
$this->assertEquals( 'en_CA.UTF-8', TTi18n::getLocale() );
$this->assertEquals( '$', TTi18n::getCurrencySymbol( 'CAD' ) );
}
function testTranslations() {
TTi18n::setLocale( 'es_ES' );
$this->assertEquals( 'Empleado', TTi18n::getText( 'Employee' ) );
TTi18n::setLocale( 'en_CA' );
$this->assertEquals( 'Employee', TTi18n::getText( 'Employee' ) );
TTi18n::setLocale( 'yi_US' );
$this->assertEquals( 'Z', TTi18n::getText( 'Employee' ) );
TTi18n::setLocale( 'fr_CA' );
//$this->assertEquals( TTi18n::getText( 'Saved Reports' ), 'Rapports sauvs' );
$this->assertEquals( 'Rapports enregistrés', TTi18n::getText( 'Saved Reports' ) );
TTi18n::setLocale( 'fr_FR' );
$this->assertEquals( 'Rapports enregistrés', TTi18n::getText( 'Saved Reports' ) );
TTi18n::setLocale( 'ar_EG' );
$this->assertEquals( 'Rapports enregistrés', TTi18n::getText( 'Saved Reports' ) ); //valid locale with no translations returns the original string.
$this->assertEquals( 'Saved ReportsZZ', TTi18n::getText( 'Saved ReportsZZ' ) ); //valid locale with no translations returns the original string.
TTi18n::setLocale( 'zz_ZZ' ); //Invalid locale gets ignored and uses the previous locale.
//$this->assertEquals( 'Saved Reports', TTi18n::getText( 'Saved Reports' ) ); //invalid locale returns the original string.
$this->assertEquals( 'Rapports enregistrés', TTi18n::getText( 'Saved Reports' ) ); //valid locale with no translations returns the original string.
$this->assertEquals( false, TTi18n::detectUTF8( 'The quick brown fox jumped over the lazy dog 1234567890!@#$%^&*()_+' ) );
$this->assertEquals( true, TTi18n::detectUTF8( 'ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ' ) );
}
function testStringFunctions() {
TTi18n::setLanguage( 'en' );
$this->assertEquals( 'test', TTi18n::strtolower( 'TesT' ) );
TTi18n::setLanguage( 'fr' );
$this->assertEquals( 'cumulé', TTi18n::strtolower( 'Cumulé' ) );
TTi18n::setLanguage( 'cn' );
$this->assertEquals( '壹', TTi18n::strtolower( '壹' ) );
}
//30,000 currrency formats on existing code took less than a second.
//30,000 currrency formats on new code took 1.24 seconds
//500,000 currrency formats on new code took 8.2 seconds
//500,000 currrency formats on old code took 9.95 seconds
// function testNumberFormatBenchmark() {
//
// TTi18n::setLocale('en_CA');
// $start_benchmark = time();
// for($i =0; $i<=500000; $i++){
// $n = TTi18n::formatCurrency($i.'.55555', 'CAD', true);
// }
// $end_benchmark = time();
// echo "time: " . ($end_benchmark - $start_benchmark);
// }
function testBCMath() {
TTi18n::setLocale( 'en_US' );
$amount1 = 510.9;
$amount2 = 90.9;
$this->assertEquals( 601.80, bcadd( $amount1, $amount2, 2 ) );
$amount1 = '510.9';
$amount2 = '90.9';
$this->assertEquals( 601.80, bcadd( $amount1, $amount2, 2 ) );
//If we switch setLocale() back to setting LC_NUMERIC, need to make sure bcmath handles comma decimal separators correctly like in UserDateTotalFactory->calcTotalAmount()
TTi18n::setLocale( 'es_ES' );
$amount1 = 510.9;
$amount2 = 90.9;
$this->assertEquals( 601.80, bcadd( $amount1, $amount2, 2 ) ); //BCMath fails handling floating point values with comma separator.
$amount1 = '510.9';
$amount2 = '90.9';
$this->assertEquals( 601.80, bcadd( $amount1, $amount2, 2 ) ); //BCMath fails handling floating point values with comma separator.
//This causes PHP fatal error on PHP v8.0.2: PHP ERROR - FATAL(1): Uncaught ValueError: bcadd(): Argument #1 ($num1) is not well-formed
//$amount1 = '510,9';
//$amount2 = '90,9';
//@$this->assertEquals( 0.00, bcadd( $amount1, $amount2, 2 ) ); //BCMath fails handling floating point values with comma separator.
//
//Test to show that bcmath() breaks when using LC_NUMERIC locales.
//
TTi18n::setLocale( 'es_ES' );
$valid_locale = setlocale( LC_ALL, TTi18n::generateLocale( 'es_ES' ) ); //Could return 'es_ES' or 'es_ES.utf8' or 'es_ES.UTF-8'
$normalized_locale = TTi18n::stripUTF8( $valid_locale );
$this->assertEquals( 'es_ES', $normalized_locale );
$amount1 = 510.9;
$amount2 = 90.9;
if ( version_compare( PHP_VERSION, '8.0', '>=' ) ) { //PHP v8.0 seems to handle comma separated floats.
$this->assertEquals( 601.80, bcadd( $amount1, $amount2, 2 ) );
} else {
@$this->assertEquals( 0.00, bcadd( $amount1, $amount2, 2 ) ); //BCMath fails handling floating point values with comma separator.
}
//This causes PHP fatal error on PHP v8.0.2: PHP ERROR - FATAL(1): Uncaught ValueError: bcadd(): Argument #1 ($num1) is not well-formed
//$amount1 = '510,9';
//$amount2 = '90,9';
//@$this->assertEquals( 0.00, bcadd( $amount1, $amount2, 2 ) ); //BCMath fails handling floating point values with comma separator.
//This causes PHP fatal error on PHP v8.0.2: PHP ERROR - FATAL(1): Uncaught ValueError: bcadd(): Argument #1 ($num1) is not well-formed
//$amount1 = '123456710,9';
//$amount2 = '90,9';
//@$this->assertEquals( 0.00, bcadd( $amount1, $amount2, 2 ) ); //BCMath fails handling floating point values with comma separator.
//Set locale back to the default so it doesn't affect other tests.
setlocale( LC_ALL, TTi18n::generateLocale( 'en_US' ) );
TTi18n::setLocale( 'en_US' );
}
}