• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7import collections
8import os
9import sys
10import unittest
11
12sys.path.insert(
13    0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)))
14from 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))
20sys.path.insert(1, _BUILD_ANDROID_GYP_ROOT)
21
22import 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
64int anim abc_fade_out 0x7f050001
65int anim abc_grow_fade_in_from_bottom 0x7f050002
66int array DefaultCookiesSettingEntries 0x7f120002
67int array DefaultCookiesSettingValues 0x7f120003
68int array DefaultGeolocationSettingEntries 0x7f120004
69int attr actionBarDivider 0x7f0100e7
70int attr actionBarStyle 0x7f0100e2
71int string AllowedDomainsForAppsDesc 0x7f0c0105
72int string AlternateErrorPagesEnabledDesc 0x7f0c0107
73int string AuthAndroidNegotiateAccountTypeDesc 0x7f0c0109
74int string AllowedDomainsForAppsTitle 0x7f0c0104
75int string AlternateErrorPagesEnabledTitle 0x7f0c0106
76int[] styleable SnackbarLayout { 0x0101011f, 0x7f010076, 0x7f0100ba }
77int styleable SnackbarLayout_android_maxWidth 0
78int 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
85int string AllowedDomainsForAppsDesc 0x7f0c0105
86int string AlternateErrorPagesEnabledDesc 0x7f0c0107
87int 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
105def _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
113class 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
274if __name__ == '__main__':
275  unittest.main()
276