#!/usr/bin/env python3
# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
'''Unit tests for misc.GritNode'''
import contextlib
import io
import os
import sys
import tempfile
import unittest
if __name__ == '__main__':
sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
from grit import grd_reader
import grit.exception
from grit import util
from grit.format import rc
from grit.format import rc_header
from grit.node import misc
@contextlib.contextmanager
def _MakeTempPredeterminedIdsFile(content):
"""Write the |content| string to a temporary file.
The temporary file must be deleted by the caller.
Example:
with _MakeTempPredeterminedIdsFile('foo') as path:
...
os.remove(path)
Args:
content: The string to write.
Yields:
The name of the temporary file.
"""
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
f.write(content)
f.flush()
f.close()
yield f.name
class GritNodeUnittest(unittest.TestCase):
@classmethod
def setUpClass(cls):
os.environ["root_gen_dir"] = "gen"
def testUniqueNameAttribute(self):
try:
restree = grd_reader.Parse(
util.PathFromRoot('grit/testdata/duplicate-name-input.xml'))
self.fail('Expected parsing exception because of duplicate names.')
except grit.exception.Parsing:
pass # Expected case
def testReadFirstIdsFromFile(self):
test_resource_ids = os.path.join(os.path.dirname(__file__), '..',
'testdata', 'resource_ids')
base_dir = os.path.dirname(test_resource_ids)
src_dir, id_dict = misc._ReadFirstIdsFromFile(
test_resource_ids,
{
'FOO': os.path.join(base_dir, 'bar'),
'SHARED_INTERMEDIATE_DIR': os.path.join(base_dir,
'out/Release/obj/gen'),
})
self.assertEqual({}, id_dict.get('bar/file.grd', None))
self.assertEqual({},
id_dict.get('out/Release/obj/gen/devtools/devtools.grd', None))
src_dir, id_dict = misc._ReadFirstIdsFromFile(
test_resource_ids,
{
'SHARED_INTERMEDIATE_DIR': '/outside/src_dir',
})
# Windows adds a c:// prefix, which is why this is needed here.
abs_path = os.path.abspath(
'/outside/src_dir/devtools/devtools.grd').replace('\\', '/')
self.assertEqual({}, id_dict.get(abs_path, None))
# Verifies that GetInputFiles() returns the correct list of files
# corresponding to ChromeScaledImage nodes when assets are missing.
def testGetInputFilesChromeScaledImage(self):
chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html')
xml = '''<?xml version="1.0" encoding="utf-8"?>
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="default.pak" type="data_package" context="default_100_percent" />
<output filename="special.pak" type="data_package" context="special_100_percent" fallback_to_default_layout="false" />
</outputs>
<release seq="1">
<structures fallback_to_low_resolution="true">
<structure type="chrome_scaled_image" name="IDR_A" file="a.png" />
<structure type="chrome_scaled_image" name="IDR_B" file="b.png" />
<structure type="chrome_html" name="HTML_FILE1" file="%s" flattenhtml="true" />
</structures>
</release>
</grit>''' % chrome_html_path
grd = grd_reader.Parse(io.StringIO(xml), util.PathFromRoot('grit/testdata'))
expected = ['chrome_html.html', 'default_100_percent/a.png',
'default_100_percent/b.png', 'included_sample.html',
'special_100_percent/a.png']
actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for
path in grd.GetInputFiles()]
# Convert path separator for Windows paths.
actual = [path.replace('\\', '/') for path in actual]
self.assertEqual(expected, actual)
# Verifies that GetInputFiles() returns the correct list of files
# when files include other files.
def testGetInputFilesFromIncludes(self):
chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html')
xml = '''<?xml version="1.0" encoding="utf-8"?>
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="default.pak" type="data_package" context="default_100_percent" />
<output filename="special.pak" type="data_package" context="special_100_percent" fallback_to_default_layout="false" />
</outputs>
<release seq="1">
<includes>
<include name="IDR_TESTDATA_CHROME_HTML" file="%s" flattenhtml="true"
allowexternalscript="true" type="BINDATA" />
</includes>
</release>
</grit>''' % chrome_html_path
grd = grd_reader.Parse(io.StringIO(xml), util.PathFromRoot('grit/testdata'))
expected = ['chrome_html.html', 'included_sample.html']
actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for
path in grd.GetInputFiles()]
# Convert path separator for Windows paths.
actual = [path.replace('\\', '/') for path in actual]
self.assertEqual(expected, actual)
def testNonDefaultEntry(self):
grd = util.ParseGrdForUnittest('''
<messages>
<message name="IDS_A" desc="foo">bar</message>
<if expr="lang == 'fr'">
<message name="IDS_B" desc="foo">bar</message>
</if>
</messages>''')
grd.SetOutputLanguage('fr')
output = ''.join(rc_header.Format(grd, 'fr', '.'))
self.assertIn('#define IDS_A 2378\n#define IDS_B 2379', output)
def testMaxIds(self):
xml = '''<?xml version="1.0" encoding="UTF-8"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="." first_ids_file="%s">
<release seq="3">
<messages>
<message name="IDS_TEST1" desc="test1">test1</message>
<message name="IDS_TEST2" desc="test2">test2</message>
</messages>
</release>
</grit>''' % 'GRIT_DIR/grit/testdata/tools/grit/resource_ids_for_overflow_test'
pseudo_file = io.StringIO(xml)
grit_root_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
'..', '..')
fake_input_path = os.path.join(
grit_root_dir, "grit/testdata/chrome/app/generated_resources.grd")
root = grd_reader.Parse(pseudo_file, os.path.split(fake_input_path)[0])
root.AssignFirstIds(fake_input_path, {})
self.assertRaises(grit.exception.IdRangeOverflow, root.InitializeIds)
def testExplicitFirstIdOverlaps(self):
# second first_id will overlap preexisting range
self.assertRaises(grit.exception.IdRangeOverlap,
util.ParseGrdForUnittest, '''
<includes first_id="300" comment="bingo">
<include type="gif" name="ID_LOGO" file="images/logo.gif" />
<include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
</includes>
<messages first_id="301">
<message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
</message>
<message name="IDS_SMURFGEBURF">Frubegfrums</message>
</messages>''')
def testImplicitOverlapsPreexisting(self):
# second message in <messages> will overlap preexisting range
self.assertRaises(grit.exception.IdRangeOverlap,
util.ParseGrdForUnittest, '''
<includes first_id="301" comment="bingo">
<include type="gif" name="ID_LOGO" file="images/logo.gif" />
<include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
</includes>
<messages first_id="300">
<message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
</message>
<message name="IDS_SMURFGEBURF">Frubegfrums</message>
</messages>''')
def testPredeterminedIds(self):
with _MakeTempPredeterminedIdsFile('IDS_A 101\nIDS_B 102') as ids_file:
grd = util.ParseGrdForUnittest('''
<includes first_id="300" comment="bingo">
<include type="gif" name="IDS_B" file="images/logo.gif" />
</includes>
<messages first_id="10000">
<message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
</message>
<message name="IDS_A">
Bongo!
</message>
</messages>''', predetermined_ids_file=ids_file)
output = rc_header.FormatDefines(grd)
self.assertEqual(('#define IDS_B 102\n'
'#define IDS_GREETING 10000\n'
'#define IDS_A 101\n'), ''.join(output))
os.remove(ids_file)
def testPredeterminedIdsOverlap(self):
with _MakeTempPredeterminedIdsFile('ID_LOGO 10000') as ids_file:
self.assertRaises(grit.exception.IdRangeOverlap,
util.ParseGrdForUnittest, '''
<includes first_id="300" comment="bingo">
<include type="gif" name="ID_LOGO" file="images/logo.gif" />
</includes>
<messages first_id="10000">
<message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
</message>
<message name="IDS_BONGO">
Bongo!
</message>
</messages>''', predetermined_ids_file=ids_file)
os.remove(ids_file)
class IfNodeUnittest(unittest.TestCase):
def testIffyness(self):
grd = grd_reader.Parse(io.StringIO('''
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<messages>
<if expr="'bingo' in defs">
<message name="IDS_BINGO">
Bingo!
</message>
</if>
<if expr="'hello' in defs">
<message name="IDS_HELLO">
Hello!
</message>
</if>
<if expr="lang == 'fr' or 'FORCE_FRENCH' in defs">
<message name="IDS_HELLO" internal_comment="French version">
Good morning
</message>
</if>
<if expr="is_win">
<message name="IDS_ISWIN">is_win</message>
</if>
</messages>
</release>
</grit>'''),
dir='.')
messages_node = grd.children[0].children[0]
bingo_message = messages_node.children[0].children[0]
hello_message = messages_node.children[1].children[0]
french_message = messages_node.children[2].children[0]
is_win_message = messages_node.children[3].children[0]
self.assertTrue(bingo_message.name == 'message')
self.assertTrue(hello_message.name == 'message')
self.assertTrue(french_message.name == 'message')
grd.SetOutputLanguage('fr')
grd.SetDefines({'hello': '1'})
active = set(grd.ActiveDescendants())
self.assertTrue(bingo_message not in active)
self.assertTrue(hello_message in active)
self.assertTrue(french_message in active)
grd.SetOutputLanguage('en')
grd.SetDefines({'bingo': 1})
active = set(grd.ActiveDescendants())
self.assertTrue(bingo_message in active)
self.assertTrue(hello_message not in active)
self.assertTrue(french_message not in active)
grd.SetOutputLanguage('en')
grd.SetDefines({'FORCE_FRENCH': '1', 'bingo': '1'})
active = set(grd.ActiveDescendants())
self.assertTrue(bingo_message in active)
self.assertTrue(hello_message not in active)
self.assertTrue(french_message in active)
grd.SetOutputLanguage('en')
grd.SetDefines({})
self.assertTrue(grd.target_platform == sys.platform)
grd.SetTargetPlatform('darwin')
active = set(grd.ActiveDescendants())
self.assertTrue(is_win_message not in active)
grd.SetTargetPlatform('win32')
active = set(grd.ActiveDescendants())
self.assertTrue(is_win_message in active)
def testElsiness(self):
grd = util.ParseGrdForUnittest('''
<messages>
<if expr="True">
<then> <message name="IDS_YES1"></message> </then>
<else> <message name="IDS_NO1"></message> </else>
</if>
<if expr="True">
<then> <message name="IDS_YES2"></message> </then>
<else> </else>
</if>
<if expr="True">
<then> </then>
<else> <message name="IDS_NO2"></message> </else>
</if>
<if expr="True">
<then> </then>
<else> </else>
</if>
<if expr="False">
<then> <message name="IDS_NO3"></message> </then>
<else> <message name="IDS_YES3"></message> </else>
</if>
<if expr="False">
<then> <message name="IDS_NO4"></message> </then>
<else> </else>
</if>
<if expr="False">
<then> </then>
<else> <message name="IDS_YES4"></message> </else>
</if>
<if expr="False">
<then> </then>
<else> </else>
</if>
</messages>''')
included = [msg.attrs['name'] for msg in grd.ActiveDescendants()
if msg.name == 'message']
self.assertEqual(['IDS_YES1', 'IDS_YES2', 'IDS_YES3', 'IDS_YES4'], included)
def testIffynessWithOutputNodes(self):
grd = grd_reader.Parse(io.StringIO('''
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<outputs>
<output filename="uncond1.rc" type="rc_data" />
<if expr="lang == 'fr' or 'hello' in defs">
<output filename="only_fr.adm" type="adm" />
<output filename="only_fr.plist" type="plist" />
</if>
<if expr="lang == 'ru'">
<output filename="doc.html" type="document" />
</if>
<output filename="uncond2.adm" type="adm" />
<output filename="iftest.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
</outputs>
</grit>'''),
dir='.')
outputs_node = grd.children[0]
uncond1_output = outputs_node.children[0]
only_fr_adm_output = outputs_node.children[1].children[0]
only_fr_plist_output = outputs_node.children[1].children[1]
doc_output = outputs_node.children[2].children[0]
uncond2_output = outputs_node.children[0]
self.assertTrue(uncond1_output.name == 'output')
self.assertTrue(only_fr_adm_output.name == 'output')
self.assertTrue(only_fr_plist_output.name == 'output')
self.assertTrue(doc_output.name == 'output')
self.assertTrue(uncond2_output.name == 'output')
grd.SetOutputLanguage('ru')
grd.SetDefines({'hello': '1'})
outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
self.assertEqual(
outputs,
['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'doc.html',
'uncond2.adm', 'iftest.h'])
grd.SetOutputLanguage('ru')
grd.SetDefines({'bingo': '2'})
outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
self.assertEqual(
outputs,
['uncond1.rc', 'doc.html', 'uncond2.adm', 'iftest.h'])
grd.SetOutputLanguage('fr')
grd.SetDefines({'hello': '1'})
outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
self.assertEqual(
outputs,
['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'uncond2.adm',
'iftest.h'])
grd.SetOutputLanguage('en')
grd.SetDefines({'bingo': '1'})
outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
self.assertEqual(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
grd.SetOutputLanguage('fr')
grd.SetDefines({'bingo': '1'})
outputs = [output.GetFilename() for output in grd.GetOutputFiles()]
self.assertNotEqual(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h'])
def testChildrenAccepted(self):
grd_reader.Parse(io.StringIO(r'''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<includes>
<if expr="'bingo' in defs">
<include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
</if>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<include type="gif" name="ID_LOGO2" file="images/logo2.gif" />
</if>
</if>
</includes>
<structures>
<if expr="'bingo' in defs">
<structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
</if>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
</if>
</if>
</structures>
<messages>
<if expr="'bingo' in defs">
<message name="IDS_BINGO">Bingo!</message>
</if>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<message name="IDS_BINGO">Bingo!</message>
</if>
</if>
</messages>
</release>
<translations>
<if expr="'bingo' in defs">
<file lang="nl" path="nl_translations.xtb" />
</if>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<file lang="nl" path="nl_translations.xtb" />
</if>
</if>
</translations>
</grit>'''),
dir='.')
def testIfBadChildrenNesting(self):
# includes
xml = io.StringIO(r'''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<includes>
<if expr="'bingo' in defs">
<structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
</if>
</includes>
</release>
</grit>''')
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
# messages
xml = io.StringIO(r'''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<messages>
<if expr="'bingo' in defs">
<structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
</if>
</messages>
</release>
</grit>''')
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
# structures
xml = io.StringIO('''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<structures>
<if expr="'bingo' in defs">
<message name="IDS_BINGO">Bingo!</message>
</if>
</structures>
</release>
</grit>''')
# translations
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
xml = io.StringIO('''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<translations>
<if expr="'bingo' in defs">
<message name="IDS_BINGO">Bingo!</message>
</if>
</translations>
</grit>''')
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
# same with nesting
xml = io.StringIO(r'''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<includes>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
</if>
</if>
</includes>
</release>
</grit>''')
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
xml = io.StringIO(r'''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<messages>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<structure type="dialog" name="IDD_ABOUTBOX" file="grit\test\data\klonk.rc" encoding="utf-16" />
</if>
</if>
</messages>
</release>
</grit>''')
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
xml = io.StringIO('''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<release seq="3">
<structures>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<message name="IDS_BINGO">Bingo!</message>
</if>
</if>
</structures>
</release>
</grit>''')
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
xml = io.StringIO('''<?xml version="1.0"?>
<grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
<translations>
<if expr="'bingo' in defs">
<if expr="'hello' in defs">
<message name="IDS_BINGO">Bingo!</message>
</if>
</if>
</translations>
</grit>''')
self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml)
class ReleaseNodeUnittest(unittest.TestCase):
def testPseudoControl(self):
grd = grd_reader.Parse(
io.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
<grit latest_public_release="1" source_lang_id="en-US" current_release="2" base_dir=".">
<release seq="1" allow_pseudo="false">
<messages>
<message name="IDS_HELLO">
Hello
</message>
</messages>
<structures>
<structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file="klonk.rc" />
</structures>
</release>
<release seq="2">
<messages>
<message name="IDS_BINGO">
Bingo
</message>
</messages>
<structures>
<structure type="menu" name="IDC_KLONKMENU" encoding="utf-16" file="klonk.rc" />
</structures>
</release>
</grit>'''), util.PathFromRoot('grit/testdata'))
grd.SetOutputLanguage('en')
grd.RunGatherers()
hello = grd.GetNodeById('IDS_HELLO')
aboutbox = grd.GetNodeById('IDD_ABOUTBOX')
bingo = grd.GetNodeById('IDS_BINGO')
menu = grd.GetNodeById('IDC_KLONKMENU')
for node in [hello, aboutbox]:
self.assertTrue(not node.PseudoIsAllowed())
for node in [bingo, menu]:
self.assertTrue(node.PseudoIsAllowed())
# TODO(benrg): There was a test here that formatting hello and aboutbox with
# a pseudo language should fail, but they do not fail and the test was
# broken and failed to catch it. Fix this.
# Should not raise an exception since pseudo is allowed
rc.FormatMessage(bingo, 'xyz-pseudo')
rc.FormatStructure(menu, 'xyz-pseudo', '.')
if __name__ == '__main__':
unittest.main()