276 lines
9.6 KiB
Python
Executable File
276 lines
9.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# coding: utf-8
|
|
# Copyright 2018 The Chromium Authors
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import collections
|
|
import os
|
|
import sys
|
|
import unittest
|
|
|
|
sys.path.insert(
|
|
0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))
|
|
from util import build_utils
|
|
|
|
# Required because the following import needs build/android/gyp in the
|
|
# Python path to import util.build_utils.
|
|
_BUILD_ANDROID_GYP_ROOT = os.path.abspath(
|
|
os.path.join(os.path.dirname(__file__), os.pardir))
|
|
sys.path.insert(1, _BUILD_ANDROID_GYP_ROOT)
|
|
|
|
import resource_utils
|
|
|
|
# pylint: disable=line-too-long
|
|
|
|
_TEST_XML_INPUT_1 = '''<?xml version="1.0" encoding="utf-8"?>
|
|
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
|
<string name="copy_to_clipboard_failure_message">"Lõikelauale kopeerimine ebaõnnestus"</string>
|
|
<string name="low_memory_error">"Eelmist toimingut ei saa vähese mälu tõttu lõpetada"</string>
|
|
<string name="opening_file_error">"Valit. faili avamine ebaõnnestus"</string>
|
|
<string name="structured_text">"This is <android:g id="STRUCTURED_TEXT">%s</android:g>"</string>
|
|
</resources>
|
|
'''
|
|
|
|
_TEST_XML_OUTPUT_2 = '''<?xml version="1.0" encoding="utf-8"?>
|
|
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
|
<string name="low_memory_error">"Eelmist toimingut ei saa vähese mälu tõttu lõpetada"</string>
|
|
<string name="structured_text">"This is <android:g id="STRUCTURED_TEXT">%s</android:g>"</string>
|
|
</resources>
|
|
'''
|
|
|
|
# pylint: enable=line-too-long
|
|
|
|
_TEST_XML_OUTPUT_EMPTY = '''<?xml version="1.0" encoding="utf-8"?>
|
|
<resources>
|
|
<!-- this file intentionally empty -->
|
|
</resources>
|
|
'''
|
|
|
|
_TEST_RESOURCES_MAP_1 = {
|
|
'low_memory_error': 'Eelmist toimingut ei saa vähese mälu tõttu lõpetada',
|
|
'opening_file_error': 'Valit. faili avamine ebaõnnestus',
|
|
'copy_to_clipboard_failure_message': 'Lõikelauale kopeerimine ebaõnnestus',
|
|
'structured_text': 'This is <android:g id="STRUCTURED_TEXT">%s</android:g>',
|
|
}
|
|
|
|
_TEST_NAMESPACES_1 = {'android': 'http://schemas.android.com/apk/res/android'}
|
|
|
|
_TEST_RESOURCES_ALLOWLIST_1 = ['low_memory_error', 'structured_text']
|
|
|
|
# Extracted from one generated Chromium R.txt file, with string resource
|
|
# names shuffled randomly.
|
|
_TEST_R_TXT = r'''int anim abc_fade_in 0x7f050000
|
|
int anim abc_fade_out 0x7f050001
|
|
int anim abc_grow_fade_in_from_bottom 0x7f050002
|
|
int array DefaultCookiesSettingEntries 0x7f120002
|
|
int array DefaultCookiesSettingValues 0x7f120003
|
|
int array DefaultGeolocationSettingEntries 0x7f120004
|
|
int attr actionBarDivider 0x7f0100e7
|
|
int attr actionBarStyle 0x7f0100e2
|
|
int string AllowedDomainsForAppsDesc 0x7f0c0105
|
|
int string AlternateErrorPagesEnabledDesc 0x7f0c0107
|
|
int string AuthAndroidNegotiateAccountTypeDesc 0x7f0c0109
|
|
int string AllowedDomainsForAppsTitle 0x7f0c0104
|
|
int string AlternateErrorPagesEnabledTitle 0x7f0c0106
|
|
int[] styleable SnackbarLayout { 0x0101011f, 0x7f010076, 0x7f0100ba }
|
|
int styleable SnackbarLayout_android_maxWidth 0
|
|
int styleable SnackbarLayout_elevation 2
|
|
'''
|
|
|
|
# Test allowlist R.txt file. Note that AlternateErrorPagesEnabledTitle is
|
|
# listed as an 'anim' and should thus be skipped. Similarly the string
|
|
# 'ThisStringDoesNotAppear' should not be in the final result.
|
|
_TEST_ALLOWLIST_R_TXT = r'''int anim AlternateErrorPagesEnabledTitle 0x7f0eeeee
|
|
int string AllowedDomainsForAppsDesc 0x7f0c0105
|
|
int string AlternateErrorPagesEnabledDesc 0x7f0c0107
|
|
int string ThisStringDoesNotAppear 0x7f0fffff
|
|
'''
|
|
|
|
_TEST_R_TEXT_RESOURCES_IDS = {
|
|
0x7f0c0105: 'AllowedDomainsForAppsDesc',
|
|
0x7f0c0107: 'AlternateErrorPagesEnabledDesc',
|
|
}
|
|
|
|
# Names of string resources in _TEST_R_TXT, should be sorted!
|
|
_TEST_R_TXT_STRING_RESOURCE_NAMES = sorted([
|
|
'AllowedDomainsForAppsDesc',
|
|
'AllowedDomainsForAppsTitle',
|
|
'AlternateErrorPagesEnabledDesc',
|
|
'AlternateErrorPagesEnabledTitle',
|
|
'AuthAndroidNegotiateAccountTypeDesc',
|
|
])
|
|
|
|
|
|
def _CreateTestFile(tmp_dir, file_name, file_data):
|
|
file_path = os.path.join(tmp_dir, file_name)
|
|
with open(file_path, 'wt') as f:
|
|
f.write(file_data)
|
|
return file_path
|
|
|
|
|
|
|
|
class ResourceUtilsTest(unittest.TestCase):
|
|
|
|
def test_GetRTxtStringResourceNames(self):
|
|
with build_utils.TempDir() as tmp_dir:
|
|
tmp_file = _CreateTestFile(tmp_dir, "test_R.txt", _TEST_R_TXT)
|
|
self.assertListEqual(
|
|
resource_utils.GetRTxtStringResourceNames(tmp_file),
|
|
_TEST_R_TXT_STRING_RESOURCE_NAMES)
|
|
|
|
def test_GenerateStringResourcesAllowList(self):
|
|
with build_utils.TempDir() as tmp_dir:
|
|
tmp_module_rtxt_file = _CreateTestFile(tmp_dir, "test_R.txt", _TEST_R_TXT)
|
|
tmp_allowlist_rtxt_file = _CreateTestFile(tmp_dir, "test_allowlist_R.txt",
|
|
_TEST_ALLOWLIST_R_TXT)
|
|
self.assertDictEqual(
|
|
resource_utils.GenerateStringResourcesAllowList(
|
|
tmp_module_rtxt_file, tmp_allowlist_rtxt_file),
|
|
_TEST_R_TEXT_RESOURCES_IDS)
|
|
|
|
def test_IsAndroidLocaleQualifier(self):
|
|
good_locales = [
|
|
'en',
|
|
'en-rUS',
|
|
'fil',
|
|
'fil-rPH',
|
|
'iw',
|
|
'iw-rIL',
|
|
'b+en',
|
|
'b+en+US',
|
|
'b+ja+Latn',
|
|
'b+ja+JP+Latn',
|
|
'b+cmn+Hant-TW',
|
|
]
|
|
bad_locales = [
|
|
'e', 'english', 'en-US', 'en_US', 'en-rus', 'b+e', 'b+english', 'b+ja+'
|
|
]
|
|
for locale in good_locales:
|
|
self.assertTrue(
|
|
resource_utils.IsAndroidLocaleQualifier(locale),
|
|
msg="'%s' should be a good locale!" % locale)
|
|
|
|
for locale in bad_locales:
|
|
self.assertFalse(
|
|
resource_utils.IsAndroidLocaleQualifier(locale),
|
|
msg="'%s' should be a bad locale!" % locale)
|
|
|
|
def test_ToAndroidLocaleName(self):
|
|
_TEST_CHROMIUM_TO_ANDROID_LOCALE_MAP = {
|
|
'en': 'en',
|
|
'en-US': 'en-rUS',
|
|
'en-FOO': 'en-rFOO',
|
|
'fil': 'tl',
|
|
'tl': 'tl',
|
|
'he': 'iw',
|
|
'he-IL': 'iw-rIL',
|
|
'id': 'in',
|
|
'id-BAR': 'in-rBAR',
|
|
'nb': 'nb',
|
|
'yi': 'ji'
|
|
}
|
|
for chromium_locale, android_locale in \
|
|
_TEST_CHROMIUM_TO_ANDROID_LOCALE_MAP.items():
|
|
result = resource_utils.ToAndroidLocaleName(chromium_locale)
|
|
self.assertEqual(result, android_locale)
|
|
|
|
def test_ToChromiumLocaleName(self):
|
|
_TEST_ANDROID_TO_CHROMIUM_LOCALE_MAP = {
|
|
'foo': 'foo',
|
|
'foo-rBAR': 'foo-BAR',
|
|
'b+lll': 'lll',
|
|
'b+ll+Extra': 'll',
|
|
'b+ll+RR': 'll-RR',
|
|
'b+lll+RR+Extra': 'lll-RR',
|
|
'b+ll+RRR+Extra': 'll-RRR',
|
|
'b+ll+Ssss': 'll-Ssss',
|
|
'b+ll+Ssss+Extra': 'll-Ssss',
|
|
'b+ll+Ssss+RR': 'll-Ssss-RR',
|
|
'b+ll+Ssss+RRR': 'll-Ssss-RRR',
|
|
'b+ll+Ssss+RRR+Extra': 'll-Ssss-RRR',
|
|
'b+ll+Whatever': 'll',
|
|
'en': 'en',
|
|
'en-rUS': 'en-US',
|
|
'en-US': None,
|
|
'en-FOO': None,
|
|
'en-rFOO': 'en-FOO',
|
|
'es-rES': 'es-ES',
|
|
'es-rUS': 'es-419',
|
|
'tl': 'fil',
|
|
'fil': 'fil',
|
|
'iw': 'he',
|
|
'iw-rIL': 'he-IL',
|
|
'b+iw+IL': 'he-IL',
|
|
'in': 'id',
|
|
'in-rBAR': 'id-BAR',
|
|
'id-rBAR': 'id-BAR',
|
|
'nb': 'nb',
|
|
'no': 'nb', # http://crbug.com/920960
|
|
}
|
|
for android_locale, chromium_locale in \
|
|
_TEST_ANDROID_TO_CHROMIUM_LOCALE_MAP.items():
|
|
result = resource_utils.ToChromiumLocaleName(android_locale)
|
|
self.assertEqual(result, chromium_locale)
|
|
|
|
def test_FindLocaleInStringResourceFilePath(self):
|
|
self.assertEqual(
|
|
None,
|
|
resource_utils.FindLocaleInStringResourceFilePath(
|
|
'res/values/whatever.xml'))
|
|
self.assertEqual(
|
|
'foo',
|
|
resource_utils.FindLocaleInStringResourceFilePath(
|
|
'res/values-foo/whatever.xml'))
|
|
self.assertEqual(
|
|
'foo-rBAR',
|
|
resource_utils.FindLocaleInStringResourceFilePath(
|
|
'res/values-foo-rBAR/whatever.xml'))
|
|
self.assertEqual(
|
|
None,
|
|
resource_utils.FindLocaleInStringResourceFilePath(
|
|
'res/values-foo/ignore-subdirs/whatever.xml'))
|
|
|
|
def test_ParseAndroidResourceStringsFromXml(self):
|
|
ret, namespaces = resource_utils.ParseAndroidResourceStringsFromXml(
|
|
_TEST_XML_INPUT_1)
|
|
self.assertDictEqual(ret, _TEST_RESOURCES_MAP_1)
|
|
self.assertDictEqual(namespaces, _TEST_NAMESPACES_1)
|
|
|
|
def test_GenerateAndroidResourceStringsXml(self):
|
|
# Fist, an empty strings map, with no namespaces
|
|
result = resource_utils.GenerateAndroidResourceStringsXml({})
|
|
self.assertEqual(result.decode('utf8'), _TEST_XML_OUTPUT_EMPTY)
|
|
|
|
result = resource_utils.GenerateAndroidResourceStringsXml(
|
|
_TEST_RESOURCES_MAP_1, _TEST_NAMESPACES_1)
|
|
self.assertEqual(result.decode('utf8'), _TEST_XML_INPUT_1)
|
|
|
|
@staticmethod
|
|
def _CreateTestResourceFile(output_dir, locale, string_map, namespaces):
|
|
values_dir = os.path.join(output_dir, 'values-' + locale)
|
|
build_utils.MakeDirectory(values_dir)
|
|
file_path = os.path.join(values_dir, 'strings.xml')
|
|
with open(file_path, 'wb') as f:
|
|
file_data = resource_utils.GenerateAndroidResourceStringsXml(
|
|
string_map, namespaces)
|
|
f.write(file_data)
|
|
return file_path
|
|
|
|
def _CheckTestResourceFile(self, file_path, expected_data):
|
|
with open(file_path) as f:
|
|
file_data = f.read()
|
|
self.assertEqual(file_data, expected_data)
|
|
|
|
def test_FilterAndroidResourceStringsXml(self):
|
|
with build_utils.TempDir() as tmp_path:
|
|
test_file = self._CreateTestResourceFile(
|
|
tmp_path, 'foo', _TEST_RESOURCES_MAP_1, _TEST_NAMESPACES_1)
|
|
resource_utils.FilterAndroidResourceStringsXml(
|
|
test_file, lambda x: x in _TEST_RESOURCES_ALLOWLIST_1)
|
|
self._CheckTestResourceFile(test_file, _TEST_XML_OUTPUT_2)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|