blob: 128b5a817075807d0747df4198d7dd555db23e8a [file] [log] [blame]
Hung-Te Lin98501ae2017-06-02 15:53:33 +08001#!/usr/bin/env python2
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
15import os
16import StringIO
17import unittest
Hung-Te Lin98501ae2017-06-02 15:53:33 +080018import logging
Hung-Te Lin76c55b22015-03-31 14:47:14 +080019
Hung-Te Lin76c55b22015-03-31 14:47:14 +080020import regions
21import yaml
22
23
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +080024_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION = (
25 'Missing %s %r; does this new data need to be added to CrOS, or '
26 'does testdata need to be updated? (just a warning, since region '
27 '%r is not a confirmed region)')
28
Hung-Te Lin76c55b22015-03-31 14:47:14 +080029class RegionTest(unittest.TestCase):
30 """Tests for the Region class."""
31
32 @classmethod
33 def _ReadTestData(cls, name):
34 """Reads a YAML-formatted test data file.
35
36 Args:
37 name: Name of file in the testdata directory.
38
39 Returns:
40 The parsed value.
41 """
42 with open(os.path.join(os.path.dirname(__file__),
43 'testdata', name + '.yaml')) as f:
44 return yaml.load(f)
45
46 @classmethod
47 def setUpClass(cls):
48 cls.locales = cls._ReadTestData('locales')
49 cls.time_zones = cls._ReadTestData('time_zones')
50 cls.migration_map = cls._ReadTestData('migration_map')
51 cls.input_methods = cls._ReadTestData('input_methods')
52
53 def _ResolveInputMethod(self, method):
54 """Resolves an input method using the migration map.
55
56 Args:
57 method: An input method ID that may contain prefixes from the
58 migration map. (E.g., "m17n:ar", which contains the "m17n:" prefix.)
59
60 Returns:
61 The input method ID after mapping any prefixes. (E.g., "m17n:ar" will
62 be mapped to "vkd_".)
63 """
64 for k, v in self.migration_map:
65 if method.startswith(k):
66 method = v + method[len(k):]
67 return method
68
69 def testZoneInfo(self):
70 all_regions = regions.BuildRegionsDict(include_all=True)
71
72 # Make sure all time zones are present in /usr/share/zoneinfo.
73 all_zoneinfos = [os.path.join('/usr/share/zoneinfo', tz)
74 for r in all_regions.values() for tz in r.time_zones]
75 missing = [z for z in all_zoneinfos if not os.path.exists(z)]
76 self.assertFalse(missing,
77 ('Missing zoneinfo files; are timezones misspelled?: %r' %
78 missing))
79
80 def testBadLocales(self):
81 self.assertRaisesRegexp(
82 AssertionError, "Locale 'en-us' does not match", regions.Region,
83 'us', 'xkb:us::eng', 'America/Los_Angeles', 'en-us', 'ANSI')
84
85 def testBadKeyboard(self):
86 self.assertRaisesRegexp(
87 AssertionError, "Keyboard pattern 'xkb:us::' does not match",
88 regions.Region, 'us', 'xkb:us::', 'America/Los_Angeles', 'en-US',
89 'ANSI')
90
91 def testKeyboardRegexp(self):
92 self.assertTrue(regions.KEYBOARD_PATTERN.match('xkb:us::eng'))
93 self.assertTrue(regions.KEYBOARD_PATTERN.match('ime:ko:korean'))
94 self.assertTrue(regions.KEYBOARD_PATTERN.match('m17n:ar'))
95 self.assertFalse(regions.KEYBOARD_PATTERN.match('m17n:'))
96 self.assertFalse(regions.KEYBOARD_PATTERN.match('foo_bar'))
97
98 def testTimeZones(self):
99 for r in regions.BuildRegionsDict(include_all=True).values():
100 for tz in r.time_zones:
101 if tz not in self.time_zones:
102 if r.region_code in regions.REGIONS:
103 self.fail(
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800104 'Missing time zones: %r; does a new time zone need to be added '
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800105 'to CrOS, or does testdata need to be updated?' % tz)
106 else:
107 # This is an unconfirmed region; just print a warning.
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800108 logging.warn(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION, 'time zone',
109 tz, r.region_code)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800110
111 def testLocales(self):
112 missing = []
113 for r in regions.BuildRegionsDict(include_all=True).values():
114 for l in r.locales:
115 if l not in self.locales:
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800116 if r.region_code in regions.REGIONS:
117 missing.append(l)
118 else:
119 logging.warn(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION, 'locale', l,
120 r.region_code)
121 self.assertFalse(missing,
122 ('Missing locale; does testdata need to be updated?: %r' %
123 missing))
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800124
125 def testInputMethods(self):
126 # Verify that every region is present in the dict.
127 for r in regions.BuildRegionsDict(include_all=True).values():
128 for k in r.keyboards:
129 resolved_method = self._ResolveInputMethod(k)
130 # Make sure the keyboard method is present.
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800131 if resolved_method not in self.input_methods:
132 if r.region_code in regions.REGIONS:
133 self.fail('Missing keyboard layout %r (resolved from %r)' % (
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800134 resolved_method, k))
Hung-Te Lin8f8e0c42015-04-13 11:42:42 +0800135 else:
136 # This is an unconfirmed region; just print a warning.
137 logging.warn(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION, 'keyboard',
138 k, r.region_code)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800139
140 def testFirmwareLocales(self):
Hung-Te Lin98501ae2017-06-02 15:53:33 +0800141 # This file is probably in src/platform2/regions
142 src_root = os.environ.get('CROS_WORKON_SRCROOT',
143 os.path.join(os.path.dirname(__file__), '..',
144 '..', '..'))
145 bmpblk_dir = os.path.join(src_root, 'src', 'platform', 'bmpblk')
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800146 if not os.path.exists(bmpblk_dir):
147 logging.warn('Skipping testFirmwareLocales, since %r is missing',
148 bmpblk_dir)
149 return
150
151 bmp_locale_dir = os.path.join(bmpblk_dir, 'strings', 'locale')
152 for r in regions.BuildRegionsDict(include_all=True).values():
153 for l in r.locales:
154 paths = [os.path.join(bmp_locale_dir, l)]
155 if '-' in l:
156 paths.append(os.path.join(bmp_locale_dir, l.partition('-')[0]))
157 if not any([os.path.exists(x) for x in paths]):
158 if r.region_code in regions.REGIONS:
159 self.fail(
160 'For region %r, none of %r exists' % (r.region_code, paths))
161 else:
162 logging.warn('For region %r, none of %r exists; '
163 'just a warning since this region is not confirmed',
164 r.region_code, paths)
165
166 def testYAMLOutput(self):
167 output = StringIO.StringIO()
168 regions.main(['--format', 'yaml'], output)
169 data = yaml.load(output.getvalue())
170 self.assertEquals(
171 {'keyboards': ['xkb:us::eng'],
172 'keyboard_mechanical_layout': 'ANSI',
173 'locales': ['en-US'],
174 'region_code': 'us',
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800175 'description': 'United States',
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800176 'regulatory_domain': 'US',
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800177 'time_zones': ['America/Los_Angeles']},
178 data['us'])
179
180 def testFieldsDict(self):
181 # 'description' and 'notes' should be missing.
182 self.assertEquals(
183 {'keyboards': ['xkb:b::b'],
184 'keyboard_mechanical_layout': 'e',
185 'description': 'description',
186 'locales': ['d'],
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800187 'region_code': 'aa',
188 'regulatory_domain': 'AA',
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800189 'time_zones': ['c']},
Hung-Te Lin98501ae2017-06-02 15:53:33 +0800190 (regions.Region('aa', 'xkb:b::b', 'c', 'd', 'e', 'description',
191 'notes').GetFieldsDict()))
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800192
193 def testConsolidateRegionsDups(self):
194 """Test duplicate handling. Two identical Regions are OK."""
195 # Make two copies of the same region.
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800196 region_list = [regions.Region('aa', 'xkb:b::b', 'c', 'd', 'e')
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800197 for _ in range(2)]
198 # It's OK.
199 self.assertEquals(
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800200 {'aa': region_list[0]}, regions.ConsolidateRegions(region_list))
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800201
202 # Modify the second copy.
203 region_list[1].keyboards = ['f']
204 # Not OK anymore!
205 self.assertRaisesRegexp(
Hung-Te Lin2c89ccd2015-04-07 15:20:35 +0800206 regions.RegionException, "Conflicting definitions for region 'aa':",
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800207 regions.ConsolidateRegions, region_list)
208
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800209if __name__ == '__main__':
Hung-Te Lin98501ae2017-06-02 15:53:33 +0800210 logging.basicConfig(format='%(message)s', level=logging.WARNING)
Hung-Te Lin76c55b22015-03-31 14:47:14 +0800211 unittest.main()