resource_utils_test.py (9800B)
1 #!/usr/bin/env python3 2 # coding: utf-8 3 # Copyright 2018 The Chromium Authors 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 7 import collections 8 import os 9 import sys 10 import unittest 11 12 sys.path.insert( 13 0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) 14 from util import build_utils 15 16 # Required because the following import needs build/android/gyp in the 17 # Python path to import util.build_utils. 18 _BUILD_ANDROID_GYP_ROOT = os.path.abspath( 19 os.path.join(os.path.dirname(__file__), os.pardir)) 20 sys.path.insert(1, _BUILD_ANDROID_GYP_ROOT) 21 22 import resource_utils 23 24 # pylint: disable=line-too-long 25 26 _TEST_XML_INPUT_1 = '''<?xml version="1.0" encoding="utf-8"?> 27 <resources xmlns:android="http://schemas.android.com/apk/res/android"> 28 <string name="copy_to_clipboard_failure_message">"Lõikelauale kopeerimine ebaõnnestus"</string> 29 <string name="low_memory_error">"Eelmist toimingut ei saa vähese mälu tõttu lõpetada"</string> 30 <string name="opening_file_error">"Valit. faili avamine ebaõnnestus"</string> 31 <string name="structured_text">"This is <android:g id="STRUCTURED_TEXT">%s</android:g>"</string> 32 </resources> 33 ''' 34 35 _TEST_XML_OUTPUT_2 = '''<?xml version="1.0" encoding="utf-8"?> 36 <resources xmlns:android="http://schemas.android.com/apk/res/android"> 37 <string name="low_memory_error">"Eelmist toimingut ei saa vähese mälu tõttu lõpetada"</string> 38 <string name="structured_text">"This is <android:g id="STRUCTURED_TEXT">%s</android:g>"</string> 39 </resources> 40 ''' 41 42 # pylint: enable=line-too-long 43 44 _TEST_XML_OUTPUT_EMPTY = '''<?xml version="1.0" encoding="utf-8"?> 45 <resources> 46 <!-- this file intentionally empty --> 47 </resources> 48 ''' 49 50 _TEST_RESOURCES_MAP_1 = { 51 'low_memory_error': 'Eelmist toimingut ei saa vähese mälu tõttu lõpetada', 52 'opening_file_error': 'Valit. faili avamine ebaõnnestus', 53 'copy_to_clipboard_failure_message': 'Lõikelauale kopeerimine ebaõnnestus', 54 'structured_text': 'This is <android:g id="STRUCTURED_TEXT">%s</android:g>', 55 } 56 57 _TEST_NAMESPACES_1 = {'android': 'http://schemas.android.com/apk/res/android'} 58 59 _TEST_RESOURCES_ALLOWLIST_1 = ['low_memory_error', 'structured_text'] 60 61 # Extracted from one generated Chromium R.txt file, with string resource 62 # names shuffled randomly. 63 _TEST_R_TXT = r'''int anim abc_fade_in 0x7f050000 64 int anim abc_fade_out 0x7f050001 65 int anim abc_grow_fade_in_from_bottom 0x7f050002 66 int array DefaultCookiesSettingEntries 0x7f120002 67 int array DefaultCookiesSettingValues 0x7f120003 68 int array DefaultGeolocationSettingEntries 0x7f120004 69 int attr actionBarDivider 0x7f0100e7 70 int attr actionBarStyle 0x7f0100e2 71 int string AllowedDomainsForAppsDesc 0x7f0c0105 72 int string AlternateErrorPagesEnabledDesc 0x7f0c0107 73 int string AuthAndroidNegotiateAccountTypeDesc 0x7f0c0109 74 int string AllowedDomainsForAppsTitle 0x7f0c0104 75 int string AlternateErrorPagesEnabledTitle 0x7f0c0106 76 int[] styleable SnackbarLayout { 0x0101011f, 0x7f010076, 0x7f0100ba } 77 int styleable SnackbarLayout_android_maxWidth 0 78 int styleable SnackbarLayout_elevation 2 79 ''' 80 81 # Test allowlist R.txt file. Note that AlternateErrorPagesEnabledTitle is 82 # listed as an 'anim' and should thus be skipped. Similarly the string 83 # 'ThisStringDoesNotAppear' should not be in the final result. 84 _TEST_ALLOWLIST_R_TXT = r'''int anim AlternateErrorPagesEnabledTitle 0x7f0eeeee 85 int string AllowedDomainsForAppsDesc 0x7f0c0105 86 int string AlternateErrorPagesEnabledDesc 0x7f0c0107 87 int string ThisStringDoesNotAppear 0x7f0fffff 88 ''' 89 90 _TEST_R_TEXT_RESOURCES_IDS = { 91 0x7f0c0105: 'AllowedDomainsForAppsDesc', 92 0x7f0c0107: 'AlternateErrorPagesEnabledDesc', 93 } 94 95 # Names of string resources in _TEST_R_TXT, should be sorted! 96 _TEST_R_TXT_STRING_RESOURCE_NAMES = sorted([ 97 'AllowedDomainsForAppsDesc', 98 'AllowedDomainsForAppsTitle', 99 'AlternateErrorPagesEnabledDesc', 100 'AlternateErrorPagesEnabledTitle', 101 'AuthAndroidNegotiateAccountTypeDesc', 102 ]) 103 104 105 def _CreateTestFile(tmp_dir, file_name, file_data): 106 file_path = os.path.join(tmp_dir, file_name) 107 with open(file_path, 'wt') as f: 108 f.write(file_data) 109 return file_path 110 111 112 113 class ResourceUtilsTest(unittest.TestCase): 114 115 def test_GetRTxtStringResourceNames(self): 116 with build_utils.TempDir() as tmp_dir: 117 tmp_file = _CreateTestFile(tmp_dir, "test_R.txt", _TEST_R_TXT) 118 self.assertListEqual( 119 resource_utils.GetRTxtStringResourceNames(tmp_file), 120 _TEST_R_TXT_STRING_RESOURCE_NAMES) 121 122 def test_GenerateStringResourcesAllowList(self): 123 with build_utils.TempDir() as tmp_dir: 124 tmp_module_rtxt_file = _CreateTestFile(tmp_dir, "test_R.txt", _TEST_R_TXT) 125 tmp_allowlist_rtxt_file = _CreateTestFile(tmp_dir, "test_allowlist_R.txt", 126 _TEST_ALLOWLIST_R_TXT) 127 self.assertDictEqual( 128 resource_utils.GenerateStringResourcesAllowList( 129 tmp_module_rtxt_file, tmp_allowlist_rtxt_file), 130 _TEST_R_TEXT_RESOURCES_IDS) 131 132 def test_IsAndroidLocaleQualifier(self): 133 good_locales = [ 134 'en', 135 'en-rUS', 136 'fil', 137 'fil-rPH', 138 'iw', 139 'iw-rIL', 140 'b+en', 141 'b+en+US', 142 'b+ja+Latn', 143 'b+ja+JP+Latn', 144 'b+cmn+Hant-TW', 145 ] 146 bad_locales = [ 147 'e', 'english', 'en-US', 'en_US', 'en-rus', 'b+e', 'b+english', 'b+ja+' 148 ] 149 for locale in good_locales: 150 self.assertTrue( 151 resource_utils.IsAndroidLocaleQualifier(locale), 152 msg="'%s' should be a good locale!" % locale) 153 154 for locale in bad_locales: 155 self.assertFalse( 156 resource_utils.IsAndroidLocaleQualifier(locale), 157 msg="'%s' should be a bad locale!" % locale) 158 159 def test_ToAndroidLocaleName(self): 160 _TEST_CHROMIUM_TO_ANDROID_LOCALE_MAP = { 161 'en': 'en', 162 'en-US': 'en-rUS', 163 'en-FOO': 'en-rFOO', 164 'fil': 'tl', 165 'tl': 'tl', 166 'he': 'iw', 167 'he-IL': 'iw-rIL', 168 'id': 'in', 169 'id-BAR': 'in-rBAR', 170 'nb': 'nb', 171 'yi': 'ji' 172 } 173 for chromium_locale, android_locale in \ 174 _TEST_CHROMIUM_TO_ANDROID_LOCALE_MAP.items(): 175 result = resource_utils.ToAndroidLocaleName(chromium_locale) 176 self.assertEqual(result, android_locale) 177 178 def test_ToChromiumLocaleName(self): 179 _TEST_ANDROID_TO_CHROMIUM_LOCALE_MAP = { 180 'foo': 'foo', 181 'foo-rBAR': 'foo-BAR', 182 'b+lll': 'lll', 183 'b+ll+Extra': 'll', 184 'b+ll+RR': 'll-RR', 185 'b+lll+RR+Extra': 'lll-RR', 186 'b+ll+RRR+Extra': 'll-RRR', 187 'b+ll+Ssss': 'll-Ssss', 188 'b+ll+Ssss+Extra': 'll-Ssss', 189 'b+ll+Ssss+RR': 'll-Ssss-RR', 190 'b+ll+Ssss+RRR': 'll-Ssss-RRR', 191 'b+ll+Ssss+RRR+Extra': 'll-Ssss-RRR', 192 'b+ll+Whatever': 'll', 193 'en': 'en', 194 'en-rUS': 'en-US', 195 'en-US': None, 196 'en-FOO': None, 197 'en-rFOO': 'en-FOO', 198 'es-rES': 'es-ES', 199 'es-rUS': 'es-419', 200 'tl': 'fil', 201 'fil': 'fil', 202 'iw': 'he', 203 'iw-rIL': 'he-IL', 204 'b+iw+IL': 'he-IL', 205 'in': 'id', 206 'in-rBAR': 'id-BAR', 207 'id-rBAR': 'id-BAR', 208 'nb': 'nb', 209 'no': 'nb', # http://crbug.com/920960 210 } 211 for android_locale, chromium_locale in \ 212 _TEST_ANDROID_TO_CHROMIUM_LOCALE_MAP.items(): 213 result = resource_utils.ToChromiumLocaleName(android_locale) 214 self.assertEqual(result, chromium_locale) 215 216 def test_FindLocaleInStringResourceFilePath(self): 217 self.assertEqual( 218 None, 219 resource_utils.FindLocaleInStringResourceFilePath( 220 'res/values/whatever.xml')) 221 self.assertEqual( 222 'foo', 223 resource_utils.FindLocaleInStringResourceFilePath( 224 'res/values-foo/whatever.xml')) 225 self.assertEqual( 226 'foo-rBAR', 227 resource_utils.FindLocaleInStringResourceFilePath( 228 'res/values-foo-rBAR/whatever.xml')) 229 self.assertEqual( 230 None, 231 resource_utils.FindLocaleInStringResourceFilePath( 232 'res/values-foo/ignore-subdirs/whatever.xml')) 233 234 def test_ParseAndroidResourceStringsFromXml(self): 235 ret, namespaces = resource_utils.ParseAndroidResourceStringsFromXml( 236 _TEST_XML_INPUT_1) 237 self.assertDictEqual(ret, _TEST_RESOURCES_MAP_1) 238 self.assertDictEqual(namespaces, _TEST_NAMESPACES_1) 239 240 def test_GenerateAndroidResourceStringsXml(self): 241 # Fist, an empty strings map, with no namespaces 242 result = resource_utils.GenerateAndroidResourceStringsXml({}) 243 self.assertEqual(result.decode('utf8'), _TEST_XML_OUTPUT_EMPTY) 244 245 result = resource_utils.GenerateAndroidResourceStringsXml( 246 _TEST_RESOURCES_MAP_1, _TEST_NAMESPACES_1) 247 self.assertEqual(result.decode('utf8'), _TEST_XML_INPUT_1) 248 249 @staticmethod 250 def _CreateTestResourceFile(output_dir, locale, string_map, namespaces): 251 values_dir = os.path.join(output_dir, 'values-' + locale) 252 build_utils.MakeDirectory(values_dir) 253 file_path = os.path.join(values_dir, 'strings.xml') 254 with open(file_path, 'wb') as f: 255 file_data = resource_utils.GenerateAndroidResourceStringsXml( 256 string_map, namespaces) 257 f.write(file_data) 258 return file_path 259 260 def _CheckTestResourceFile(self, file_path, expected_data): 261 with open(file_path) as f: 262 file_data = f.read() 263 self.assertEqual(file_data, expected_data) 264 265 def test_FilterAndroidResourceStringsXml(self): 266 with build_utils.TempDir() as tmp_path: 267 test_file = self._CreateTestResourceFile( 268 tmp_path, 'foo', _TEST_RESOURCES_MAP_1, _TEST_NAMESPACES_1) 269 resource_utils.FilterAndroidResourceStringsXml( 270 test_file, lambda x: x in _TEST_RESOURCES_ALLOWLIST_1) 271 self._CheckTestResourceFile(test_file, _TEST_XML_OUTPUT_2) 272 273 274 if __name__ == '__main__': 275 unittest.main()