blob: 128b5a817075807d0747df4198d7dd555db23e8a [file] [log] [blame]
#!/usr/bin/env python2
#
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for regions.py.
These tests ensure that all regions in regions.py are valid. The tests use
testdata pulled from the Chromium sources.
"""
from __future__ import print_function
import os
import StringIO
import unittest
import logging
import regions
import yaml
_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION = (
'Missing %s %r; does this new data need to be added to CrOS, or '
'does testdata need to be updated? (just a warning, since region '
'%r is not a confirmed region)')
class RegionTest(unittest.TestCase):
"""Tests for the Region class."""
@classmethod
def _ReadTestData(cls, name):
"""Reads a YAML-formatted test data file.
Args:
name: Name of file in the testdata directory.
Returns:
The parsed value.
"""
with open(os.path.join(os.path.dirname(__file__),
'testdata', name + '.yaml')) as f:
return yaml.load(f)
@classmethod
def setUpClass(cls):
cls.locales = cls._ReadTestData('locales')
cls.time_zones = cls._ReadTestData('time_zones')
cls.migration_map = cls._ReadTestData('migration_map')
cls.input_methods = cls._ReadTestData('input_methods')
def _ResolveInputMethod(self, method):
"""Resolves an input method using the migration map.
Args:
method: An input method ID that may contain prefixes from the
migration map. (E.g., "m17n:ar", which contains the "m17n:" prefix.)
Returns:
The input method ID after mapping any prefixes. (E.g., "m17n:ar" will
be mapped to "vkd_".)
"""
for k, v in self.migration_map:
if method.startswith(k):
method = v + method[len(k):]
return method
def testZoneInfo(self):
all_regions = regions.BuildRegionsDict(include_all=True)
# Make sure all time zones are present in /usr/share/zoneinfo.
all_zoneinfos = [os.path.join('/usr/share/zoneinfo', tz)
for r in all_regions.values() for tz in r.time_zones]
missing = [z for z in all_zoneinfos if not os.path.exists(z)]
self.assertFalse(missing,
('Missing zoneinfo files; are timezones misspelled?: %r' %
missing))
def testBadLocales(self):
self.assertRaisesRegexp(
AssertionError, "Locale 'en-us' does not match", regions.Region,
'us', 'xkb:us::eng', 'America/Los_Angeles', 'en-us', 'ANSI')
def testBadKeyboard(self):
self.assertRaisesRegexp(
AssertionError, "Keyboard pattern 'xkb:us::' does not match",
regions.Region, 'us', 'xkb:us::', 'America/Los_Angeles', 'en-US',
'ANSI')
def testKeyboardRegexp(self):
self.assertTrue(regions.KEYBOARD_PATTERN.match('xkb:us::eng'))
self.assertTrue(regions.KEYBOARD_PATTERN.match('ime:ko:korean'))
self.assertTrue(regions.KEYBOARD_PATTERN.match('m17n:ar'))
self.assertFalse(regions.KEYBOARD_PATTERN.match('m17n:'))
self.assertFalse(regions.KEYBOARD_PATTERN.match('foo_bar'))
def testTimeZones(self):
for r in regions.BuildRegionsDict(include_all=True).values():
for tz in r.time_zones:
if tz not in self.time_zones:
if r.region_code in regions.REGIONS:
self.fail(
'Missing time zones: %r; does a new time zone need to be added '
'to CrOS, or does testdata need to be updated?' % tz)
else:
# This is an unconfirmed region; just print a warning.
logging.warn(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION, 'time zone',
tz, r.region_code)
def testLocales(self):
missing = []
for r in regions.BuildRegionsDict(include_all=True).values():
for l in r.locales:
if l not in self.locales:
if r.region_code in regions.REGIONS:
missing.append(l)
else:
logging.warn(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION, 'locale', l,
r.region_code)
self.assertFalse(missing,
('Missing locale; does testdata need to be updated?: %r' %
missing))
def testInputMethods(self):
# Verify that every region is present in the dict.
for r in regions.BuildRegionsDict(include_all=True).values():
for k in r.keyboards:
resolved_method = self._ResolveInputMethod(k)
# Make sure the keyboard method is present.
if resolved_method not in self.input_methods:
if r.region_code in regions.REGIONS:
self.fail('Missing keyboard layout %r (resolved from %r)' % (
resolved_method, k))
else:
# This is an unconfirmed region; just print a warning.
logging.warn(_WARN_UNKNOWN_DATA_IN_UNCONFIRMED_REGION, 'keyboard',
k, r.region_code)
def testFirmwareLocales(self):
# This file is probably in src/platform2/regions
src_root = os.environ.get('CROS_WORKON_SRCROOT',
os.path.join(os.path.dirname(__file__), '..',
'..', '..'))
bmpblk_dir = os.path.join(src_root, 'src', 'platform', 'bmpblk')
if not os.path.exists(bmpblk_dir):
logging.warn('Skipping testFirmwareLocales, since %r is missing',
bmpblk_dir)
return
bmp_locale_dir = os.path.join(bmpblk_dir, 'strings', 'locale')
for r in regions.BuildRegionsDict(include_all=True).values():
for l in r.locales:
paths = [os.path.join(bmp_locale_dir, l)]
if '-' in l:
paths.append(os.path.join(bmp_locale_dir, l.partition('-')[0]))
if not any([os.path.exists(x) for x in paths]):
if r.region_code in regions.REGIONS:
self.fail(
'For region %r, none of %r exists' % (r.region_code, paths))
else:
logging.warn('For region %r, none of %r exists; '
'just a warning since this region is not confirmed',
r.region_code, paths)
def testYAMLOutput(self):
output = StringIO.StringIO()
regions.main(['--format', 'yaml'], output)
data = yaml.load(output.getvalue())
self.assertEquals(
{'keyboards': ['xkb:us::eng'],
'keyboard_mechanical_layout': 'ANSI',
'locales': ['en-US'],
'region_code': 'us',
'description': 'United States',
'regulatory_domain': 'US',
'time_zones': ['America/Los_Angeles']},
data['us'])
def testFieldsDict(self):
# 'description' and 'notes' should be missing.
self.assertEquals(
{'keyboards': ['xkb:b::b'],
'keyboard_mechanical_layout': 'e',
'description': 'description',
'locales': ['d'],
'region_code': 'aa',
'regulatory_domain': 'AA',
'time_zones': ['c']},
(regions.Region('aa', 'xkb:b::b', 'c', 'd', 'e', 'description',
'notes').GetFieldsDict()))
def testConsolidateRegionsDups(self):
"""Test duplicate handling. Two identical Regions are OK."""
# Make two copies of the same region.
region_list = [regions.Region('aa', 'xkb:b::b', 'c', 'd', 'e')
for _ in range(2)]
# It's OK.
self.assertEquals(
{'aa': region_list[0]}, regions.ConsolidateRegions(region_list))
# Modify the second copy.
region_list[1].keyboards = ['f']
# Not OK anymore!
self.assertRaisesRegexp(
regions.RegionException, "Conflicting definitions for region 'aa':",
regions.ConsolidateRegions, region_list)
if __name__ == '__main__':
logging.basicConfig(format='%(message)s', level=logging.WARNING)
unittest.main()