blob: eb7c6abae220253485635af2abecbcd68b4d0d21 [file] [log] [blame]
Yilin Yang8e1b24c2020-02-03 15:02:25 +08001#!/usr/bin/env python3
Hung-Te Lin76c55b22015-03-31 14:47:14 +08002#
3# Copyright 2015 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Tests for regions.py.
8
9These tests ensure that all regions in regions.py are valid. The tests use
10testdata pulled from the Chromium sources.
11"""
12
13from __future__ import print_function
14
Yilin Yang7b242ae2020-02-03 15:19:25 +080015import io
Hung-Te Lin76c55b22015-03-31 14:47:14 +080016import os
Pin-Yen Lin0825b262021-01-14 15:52:55 +080017import sys
Hung-Te Lin76c55b22015-03-31 14:47:14 +080018import unittest
Pin-Yen Lin0825b262021-01-14 15:52:55 +080019import yaml # pylint: disable=import-error
Hung-Te Lin76c55b22015-03-31 14:47:14 +080020
Pin-Yen Lin0825b262021-01-14 15:52:55 +080021# Find chromite!
22sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)),
23 '..', '..', '..'))
24
25# pylint: disable=wrong-import-position
26from chromite.lib import cros_logging as logging
Hung-Te Lin76c55b22015-03-31 14:47:14 +080027import regions
Pin-Yen Lin0825b262021-01-14 15:52:55 +080028# pylint: enable=wrong-import-position
Hung-Te Lin76c55b22015-03-31 14:47:14 +080029
30
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +080031_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION = (
32 'Missing %s %r; does this new data need to be added to CrOS, or '
33 'does testdata need to be updated? (just a warning, since region '
34 '%r is not a confirmed region)')
35
Pin-Yen Lin0825b262021-01-14 15:52:55 +080036CustomLoader = yaml.SafeLoader
37CustomLoader.add_constructor('tag:yaml.org,2002:python/tuple',
38 CustomLoader.construct_yaml_seq)
39
Hung-Te Lin76c55b22015-03-31 14:47:14 +080040class RegionTest(unittest.TestCase):
41 """Tests for the Region class."""
42
43 @classmethod
44 def _ReadTestData(cls, name):
45 """Reads a YAML-formatted test data file.
46
47 Args:
48 name: Name of file in the testdata directory.
49
50 Returns:
51 The parsed value.
52 """
53 with open(os.path.join(os.path.dirname(__file__),
54 'testdata', name + '.yaml')) as f:
Pin-Yen Lin0825b262021-01-14 15:52:55 +080055 return yaml.load(f, Loader=CustomLoader)
Hung-Te Lin76c55b22015-03-31 14:47:14 +080056
57 @classmethod
58 def setUpClass(cls):
59 cls.locales = cls._ReadTestData('locales')
60 cls.time_zones = cls._ReadTestData('time_zones')
61 cls.migration_map = cls._ReadTestData('migration_map')
62 cls.input_methods = cls._ReadTestData('input_methods')
63
64 def _ResolveInputMethod(self, method):
65 """Resolves an input method using the migration map.
66
67 Args:
68 method: An input method ID that may contain prefixes from the
69 migration map. (E.g., "m17n:ar", which contains the "m17n:" prefix.)
70
71 Returns:
72 The input method ID after mapping any prefixes. (E.g., "m17n:ar" will
73 be mapped to "vkd_".)
74 """
75 for k, v in self.migration_map:
76 if method.startswith(k):
77 method = v + method[len(k):]
78 return method
79
80 def testZoneInfo(self):
81 all_regions = regions.BuildRegionsDict(include_all=True)
82
83 # Make sure all time zones are present in /usr/share/zoneinfo.
84 all_zoneinfos = [os.path.join('/usr/share/zoneinfo', tz)
85 for r in all_regions.values() for tz in r.time_zones]
86 missing = [z for z in all_zoneinfos if not os.path.exists(z)]
87 self.assertFalse(missing,
88 ('Missing zoneinfo files; are timezones misspelled?: %r' %
89 missing))
90
91 def testBadLocales(self):
Yilin Yang4d719b32020-02-03 15:11:08 +080092 self.assertRaisesRegex(
Hung-Te Lin76c55b22015-03-31 14:47:14 +080093 AssertionError, "Locale 'en-us' does not match", regions.Region,
94 'us', 'xkb:us::eng', 'America/Los_Angeles', 'en-us', 'ANSI')
95
96 def testBadKeyboard(self):
Yilin Yang4d719b32020-02-03 15:11:08 +080097 self.assertRaisesRegex(
Hung-Te Lin76c55b22015-03-31 14:47:14 +080098 AssertionError, "Keyboard pattern 'xkb:us::' does not match",
99 regions.Region, 'us', 'xkb:us::', 'America/Los_Angeles', 'en-US',
100 'ANSI')
101
102 def testKeyboardRegexp(self):
103 self.assertTrue(regions.KEYBOARD_PATTERN.match('xkb:us::eng'))
104 self.assertTrue(regions.KEYBOARD_PATTERN.match('ime:ko:korean'))
105 self.assertTrue(regions.KEYBOARD_PATTERN.match('m17n:ar'))
106 self.assertFalse(regions.KEYBOARD_PATTERN.match('m17n:'))
107 self.assertFalse(regions.KEYBOARD_PATTERN.match('foo_bar'))
108
109 def testTimeZones(self):
110 for r in regions.BuildRegionsDict(include_all=True).values():
111 for tz in r.time_zones:
112 if tz not in self.time_zones:
113 if r.region_code in regions.REGIONS:
114 self.fail(
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800115 'Missing time zones: %r; does a new time zone need to be added '
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800116 'to CrOS, or does testdata need to be updated?' % tz)
117 else:
118 # This is an unconfirmed region; just print a warning.
Pin-Yen Lin0825b262021-01-14 15:52:55 +0800119 logging.warning(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION,
120 'time zone', tz, r.region_code)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800121
122 def testLocales(self):
123 missing = []
124 for r in regions.BuildRegionsDict(include_all=True).values():
125 for l in r.locales:
126 if l not in self.locales:
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800127 if r.region_code in regions.REGIONS:
128 missing.append(l)
129 else:
Pin-Yen Lin0825b262021-01-14 15:52:55 +0800130 logging.warning(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION, 'locale',
131 l, r.region_code)
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800132 self.assertFalse(missing,
133 ('Missing locale; does testdata need to be updated?: %r' %
134 missing))
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800135
136 def testInputMethods(self):
137 # Verify that every region is present in the dict.
138 for r in regions.BuildRegionsDict(include_all=True).values():
139 for k in r.keyboards:
140 resolved_method = self._ResolveInputMethod(k)
141 # Make sure the keyboard method is present.
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800142 if resolved_method not in self.input_methods:
143 if r.region_code in regions.REGIONS:
144 self.fail('Missing keyboard layout %r (resolved from %r)' % (
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800145 resolved_method, k))
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800146 else:
147 # This is an unconfirmed region; just print a warning.
Pin-Yen Lin0825b262021-01-14 15:52:55 +0800148 logging.warning(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION,
149 'keyboard', k, r.region_code)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800150
151 def testFirmwareLocales(self):
Hung-Te Lin98501ae2017-06-02 15:53:33 +0800152 # This file is probably in src/platform2/regions
153 src_root = os.environ.get('CROS_WORKON_SRCROOT',
154 os.path.join(os.path.dirname(__file__), '..',
155 '..', '..'))
156 bmpblk_dir = os.path.join(src_root, 'src', 'platform', 'bmpblk')
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800157 if not os.path.exists(bmpblk_dir):
Pin-Yen Lin0825b262021-01-14 15:52:55 +0800158 logging.warning('Skipping testFirmwareLocales, since %r is missing',
159 bmpblk_dir)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800160 return
161
162 bmp_locale_dir = os.path.join(bmpblk_dir, 'strings', 'locale')
163 for r in regions.BuildRegionsDict(include_all=True).values():
Marco Chen95a5a872019-05-13 23:52:56 +0800164 checked_paths = []
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800165 for l in r.locales:
166 paths = [os.path.join(bmp_locale_dir, l)]
167 if '-' in l:
168 paths.append(os.path.join(bmp_locale_dir, l.partition('-')[0]))
Marco Chen95a5a872019-05-13 23:52:56 +0800169 if any([os.path.exists(x) for x in paths]):
170 break
171 checked_paths += paths
172 else:
173 if r.region_code in regions.REGIONS:
174 self.fail(
175 'For region %r, none of %r exists' % (r.region_code,
176 checked_paths))
177 else:
Pin-Yen Lin0825b262021-01-14 15:52:55 +0800178 logging.warning('For region %r, none of %r exists; '
179 'just a warning since this region is not confirmed',
180 r.region_code, checked_paths)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800181
182 def testYAMLOutput(self):
Yilin Yang7b242ae2020-02-03 15:19:25 +0800183 output = io.StringIO()
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800184 regions.main(['--format', 'yaml'], output)
Pin-Yen Lin0825b262021-01-14 15:52:55 +0800185 data = yaml.load(output.getvalue(), Loader=CustomLoader)
Yilin Yangbb9e1ec2020-02-03 15:11:48 +0800186 self.assertEqual(
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800187 {'keyboards': ['xkb:us::eng'],
188 'keyboard_mechanical_layout': 'ANSI',
189 'locales': ['en-US'],
190 'region_code': 'us',
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800191 'description': 'United States',
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800192 'regulatory_domain': 'US',
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800193 'time_zones': ['America/Los_Angeles']},
194 data['us'])
195
196 def testFieldsDict(self):
197 # 'description' and 'notes' should be missing.
Yilin Yangbb9e1ec2020-02-03 15:11:48 +0800198 self.assertEqual(
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800199 {'keyboards': ['xkb:b::b'],
200 'keyboard_mechanical_layout': 'e',
201 'description': 'description',
202 'locales': ['d'],
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800203 'region_code': 'aa',
204 'regulatory_domain': 'AA',
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800205 'time_zones': ['c']},
Hung-Te Lin98501ae2017-06-02 15:53:33 +0800206 (regions.Region('aa', 'xkb:b::b', 'c', 'd', 'e', 'description',
207 'notes').GetFieldsDict()))
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800208
209 def testConsolidateRegionsDups(self):
210 """Test duplicate handling. Two identical Regions are OK."""
211 # Make two copies of the same region.
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800212 region_list = [regions.Region('aa', 'xkb:b::b', 'c', 'd', 'e')
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800213 for _ in range(2)]
214 # It's OK.
Yilin Yangbb9e1ec2020-02-03 15:11:48 +0800215 self.assertEqual(
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800216 {'aa': region_list[0]}, regions.ConsolidateRegions(region_list))
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800217
218 # Modify the second copy.
219 region_list[1].keyboards = ['f']
220 # Not OK anymore!
Yilin Yang4d719b32020-02-03 15:11:08 +0800221 self.assertRaisesRegex(
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800222 regions.RegionException, "Conflicting definitions for region 'aa':",
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800223 regions.ConsolidateRegions, region_list)
224
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800225if __name__ == '__main__':
Hung-Te Lin98501ae2017-06-02 15:53:33 +0800226 logging.basicConfig(format='%(message)s', level=logging.WARNING)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800227 unittest.main()