This commit is contained in:
Paul Holden 2022-12-14 13:22:01 +00:00
commit 5158be1b64
2 changed files with 115 additions and 12 deletions

View File

@ -79,6 +79,19 @@ class locale {
* @return string|false Returns the new current locale, or FALSE on error.
*/
protected function set_locale(int $category = LC_ALL, string $locale = '0') {
return setlocale($category, $locale);
if (strlen($locale) <= 255 || PHP_OS_FAMILY === 'BSD' || PHP_OS_FAMILY === 'Darwin') {
// We can set the whole locale all together.
return setlocale($category, $locale);
}
// Too long locale with linux or windows, let's split it into known and supported categories.
$split = explode(';', $locale);
foreach ($split as $element) {
[$category, $value] = explode('=', $element);
if (defined($category)) { // Only if the category exists, there are OS differences.
setlocale(constant($category), $value);
}
}
return setlocale(LC_ALL, 0); // Finally, return the complete configured locale.
}
}

View File

@ -14,14 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tests for \tool_langimport\locale class.
*
* @package tool_langimport
* @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_langimport;
/**
@ -29,6 +21,7 @@ namespace tool_langimport;
*
* @package tool_langimport
* @category test
* @coversDefaultClass \tool_langimport\locale
* @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@ -36,6 +29,7 @@ class locale_test extends \advanced_testcase {
/**
* Test that \tool_langimport\locale::check_locale_availability() works as expected.
*
* @covers ::check_locale_availability
* @return void
*/
public function test_check_locale_availability() {
@ -43,7 +37,7 @@ class locale_test extends \advanced_testcase {
// - first setlocale() call which backup current locale
// - second setlocale() call which try to set new 'es' locale
// - third setlocale() call which restore locale.
$mock = $this->getMockBuilder(\tool_langimport\locale::class)
$mock = $this->getMockBuilder(locale::class)
->onlyMethods(['set_locale'])
->getMock();
$mock->method('set_locale')->will($this->onConsecutiveCalls('en', 'es', 'en'));
@ -56,7 +50,7 @@ class locale_test extends \advanced_testcase {
// - first setlocale() call which backup current locale
// - second setlocale() call which fail to set new locale
// - third setlocale() call which restore locale.
$mock = $this->getMockBuilder(\tool_langimport\locale::class)
$mock = $this->getMockBuilder(locale::class)
->onlyMethods(['set_locale'])
->getMock();
$mock->method('set_locale')->will($this->onConsecutiveCalls('en', false, 'en'));
@ -66,8 +60,104 @@ class locale_test extends \advanced_testcase {
$this->assertFalse($result);
// Test an invalid parameter.
$locale = new \tool_langimport\locale();
$locale = new locale();
$this->expectException(\coding_exception::class);
$locale->check_locale_availability('');
}
/**
* Test \tool_langimport\locale::set_locale() own logic.
*
* We have to explicitly test set_locale() own logic and results,
* that effectively sets the current locale, so we need to restore
* the original locale after every test (ugly, from a purist unit test
* point of view, but needed).
*
* @dataProvider set_locale_provider
* @covers ::set_locale
*
* @param string $set locale string to be set.
* @param string $ret expected results returned after setting the locale.
*/
public function test_set_locale(string $set, string $ret) {
// Make set_locale() public.
$loc = new locale();
$rc = new \ReflectionClass(locale::class);
$rm = $rc->getMethod('set_locale');
$rm->setAccessible(true);
// Capture current locale for later restore (funnily, using the set_locale() method itself.
$originallocale = $rm->invokeArgs($loc, [LC_ALL, 0]);
// Assert we get the locale defined as expected.
$this->assertEquals($ret, $rm->invokeArgs($loc, [LC_ALL, $set]));
// We have finished, restore the original locale, so this doesn't affect other tests at distance.
// (again, funnily, using the very same set_locale() method).
$rm->invokeArgs($loc, [LC_ALL, $originallocale]);
}
/**
* Data provider for test_set_locale().
*
* Provides a locale to be set (as 'set') and a expected return value (as 'ret'). Note that
* some of the locales are OS dependent, so only the ones matching the OS will be provided.
*
* We make extensive use of the en_AU.UTF-8/English_Australia.1252 locale that is mandatory to
* be installed in any system running PHPUnit tests.
*/
public function set_locale_provider(): array {
// Let's list the allowed categories by OS.
$bsdallowed = ['LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME'];
$winallowed = ['LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME'];
$linuxallowed = [
'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME',
'LC_PAPER', 'LC_NAME', 'LC_ADDRESS', 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION'
];
// The base locale name is also OS dependent.
$baselocale = get_string('locale', 'langconfig');
if (PHP_OS_FAMILY === 'Windows') {
$baselocale = get_string('localewin', 'langconfig');
}
// Here we'll go accumulating cases to be provided.
$cases = [];
// First, the simplest case, just pass a locale name, without categories.
$cases['rawlocale'] = [
'set' => $baselocale,
'ret' => $baselocale,
];
// Now, let's fill ALL LC categories, we should get back the locale name if all them are set with same value.
// Note that this case is the one that, under Linux only, covers the changes performed to the set_locale() method.
// Pick the correct categories depending on the OS.
$oscategories = $bsdallowed; // Default to BSD/Dawrwin ones because they are the standard 6 supported by PHP.
if (PHP_OS_FAMILY === 'Windows') {
$oscategories = $winallowed;
} else if (PHP_OS_FAMILY === 'Linux') {
$oscategories = $linuxallowed;
}
$localestr = '';
foreach ($oscategories as $category) {
// Format is different by OS too, so let build the string conditionally.
if (PHP_OS_FAMILY === 'BSD' || PHP_OS_FAMILY === 'Darwin') {
// BSD uses slashes (/) separated list of the 6 values in exact order.
$localestr .= '/' . $baselocale;
} else {
// Linux/Windows use semicolon (;) separated list of category=value pairs.
$localestr .= ';' . $category . '=' . $baselocale;
}
}
$cases['allcategories'] = [
'set' => trim($localestr, ';/'),
'ret' => $baselocale,
];
// Return all the built cases.
return $cases;
}
}