1#!/usr/bin/env python
2# Copyright (c) 2011 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6'''Unit tests for grit.format.rc'''
7
8import os
9import re
10import sys
11if __name__ == '__main__':
12  sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
13
14import tempfile
15import unittest
16import StringIO
17
18from grit import grd_reader
19from grit import util
20from grit.node import structure
21from grit.tool import build
22
23
24_PREAMBLE = '''\
25#include "resource.h"
26#include <winresrc.h>
27#ifdef IDC_STATIC
28#undef IDC_STATIC
29#endif
30#define IDC_STATIC (-1)
31'''
32
33
34class DummyOutput(object):
35  def __init__(self, type, language, file = 'hello.gif'):
36    self.type = type
37    self.language = language
38    self.file = file
39  def GetType(self):
40    return self.type
41  def GetLanguage(self):
42    return self.language
43  def GetOutputFilename(self):
44    return self.file
45
46class FormatRcUnittest(unittest.TestCase):
47  def testMessages(self):
48    root = util.ParseGrdForUnittest('''
49      <messages>
50          <message name="IDS_BTN_GO" desc="Button text" meaning="verb">Go!</message>
51          <message name="IDS_GREETING" desc="Printed to greet the currently logged in user">
52            Hello <ph name="USERNAME">%s<ex>Joi</ex></ph>, how are you doing today?
53          </message>
54          <message name="BONGO" desc="Flippo nippo">
55            Howdie "Mr. Elephant", how are you doing?   \'\'\'
56          </message>
57          <message name="IDS_WITH_LINEBREAKS">
58Good day sir,
59I am a bee
60Sting sting
61          </message>
62      </messages>
63      ''')
64
65    buf = StringIO.StringIO()
66    build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
67    output = util.StripBlankLinesAndComments(buf.getvalue())
68    self.assertEqual(_PREAMBLE + u'''\
69STRINGTABLE
70BEGIN
71  IDS_BTN_GO      "Go!"
72  IDS_GREETING    "Hello %s, how are you doing today?"
73  BONGO           "Howdie ""Mr. Elephant"", how are you doing?   "
74  IDS_WITH_LINEBREAKS "Good day sir,\\nI am a bee\\nSting sting"
75END''', output)
76
77
78  def testRcSection(self):
79    root = util.ParseGrdForUnittest('''
80      <structures>
81          <structure type="menu" name="IDC_KLONKMENU" file="grit\\testdata\klonk.rc" encoding="utf-16" />
82          <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\testdata\klonk.rc" encoding="utf-16" />
83          <structure type="version" name="VS_VERSION_INFO" file="grit\\testdata\klonk.rc" encoding="utf-16" />
84      </structures>''')
85    root.SetOutputLanguage('en')
86    root.RunGatherers()
87
88    buf = StringIO.StringIO()
89    build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
90    output = util.StripBlankLinesAndComments(buf.getvalue())
91    expected = _PREAMBLE + u'''\
92IDC_KLONKMENU MENU
93BEGIN
94    POPUP "&File"
95    BEGIN
96        MENUITEM "E&xit",                       IDM_EXIT
97        MENUITEM "This be ""Klonk"" me like",   ID_FILE_THISBE
98        POPUP "gonk"
99        BEGIN
100            MENUITEM "Klonk && is [good]",           ID_GONK_KLONKIS
101        END
102    END
103    POPUP "&Help"
104    BEGIN
105        MENUITEM "&About ...",                  IDM_ABOUT
106    END
107END
108
109IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
110STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
111CAPTION "About"
112FONT 8, "System", 0, 0, 0x0
113BEGIN
114    ICON            IDI_KLONK,IDC_MYICON,14,9,20,20
115    LTEXT           "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
116                    SS_NOPREFIX
117    LTEXT           "Copyright (C) 2005",IDC_STATIC,49,20,119,8
118    DEFPUSHBUTTON   "OK",IDOK,195,6,30,11,WS_GROUP
119    CONTROL         "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
120                    BS_AUTORADIOBUTTON,46,51,84,10
121END
122
123VS_VERSION_INFO VERSIONINFO
124 FILEVERSION 1,0,0,1
125 PRODUCTVERSION 1,0,0,1
126 FILEFLAGSMASK 0x17L
127#ifdef _DEBUG
128 FILEFLAGS 0x1L
129#else
130 FILEFLAGS 0x0L
131#endif
132 FILEOS 0x4L
133 FILETYPE 0x1L
134 FILESUBTYPE 0x0L
135BEGIN
136    BLOCK "StringFileInfo"
137    BEGIN
138        BLOCK "040904b0"
139        BEGIN
140            VALUE "FileDescription", "klonk Application"
141            VALUE "FileVersion", "1, 0, 0, 1"
142            VALUE "InternalName", "klonk"
143            VALUE "LegalCopyright", "Copyright (C) 2005"
144            VALUE "OriginalFilename", "klonk.exe"
145            VALUE "ProductName", " klonk Application"
146            VALUE "ProductVersion", "1, 0, 0, 1"
147        END
148    END
149    BLOCK "VarFileInfo"
150    BEGIN
151        VALUE "Translation", 0x409, 1200
152    END
153END'''.strip()
154    for expected_line, output_line in zip(expected.split(), output.split()):
155      self.assertEqual(expected_line, output_line)
156
157  def testRcIncludeStructure(self):
158    root = util.ParseGrdForUnittest('''
159      <structures>
160        <structure type="tr_html" name="IDR_HTML" file="bingo.html"/>
161        <structure type="tr_html" name="IDR_HTML2" file="bingo2.html"/>
162      </structures>''', base_dir = '/temp')
163    # We do not run gatherers as it is not needed and wouldn't find the file
164
165    buf = StringIO.StringIO()
166    build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
167    output = util.StripBlankLinesAndComments(buf.getvalue())
168    expected = (_PREAMBLE +
169                u'IDR_HTML           HTML               "%s"\n'
170                u'IDR_HTML2          HTML               "%s"'
171                % (util.normpath('/temp/bingo.html').replace('\\', '\\\\'),
172                   util.normpath('/temp/bingo2.html').replace('\\', '\\\\')))
173    # hackety hack to work on win32&lin
174    output = re.sub('"[c-zC-Z]:', '"', output)
175    self.assertEqual(expected, output)
176
177  def testRcIncludeFile(self):
178    root = util.ParseGrdForUnittest('''
179      <includes>
180        <include type="TXT" name="TEXT_ONE" file="bingo.txt"/>
181        <include type="TXT" name="TEXT_TWO" file="bingo2.txt"  filenameonly="true" />
182      </includes>''', base_dir = '/temp')
183
184    buf = StringIO.StringIO()
185    build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
186    output = util.StripBlankLinesAndComments(buf.getvalue())
187    expected = (_PREAMBLE +
188                u'TEXT_ONE           TXT                "%s"\n'
189                u'TEXT_TWO           TXT                "%s"'
190                % (util.normpath('/temp/bingo.txt').replace('\\', '\\\\'),
191                   'bingo2.txt'))
192    # hackety hack to work on win32&lin
193    output = re.sub('"[c-zC-Z]:', '"', output)
194    self.assertEqual(expected, output)
195
196  def testRcIncludeFlattenedHtmlFile(self):
197    input_file = util.PathFromRoot('grit/testdata/include_test.html')
198    output_file = '%s/HTML_FILE1_include_test.html' % tempfile.gettempdir()
199    root = util.ParseGrdForUnittest('''
200      <includes>
201        <include name="HTML_FILE1" flattenhtml="true" file="%s" type="BINDATA" />
202      </includes>''' % input_file)
203
204    buf = StringIO.StringIO()
205    build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en', output_file),
206                                buf)
207    output = util.StripBlankLinesAndComments(buf.getvalue())
208
209    expected = (_PREAMBLE +
210        u'HTML_FILE1         BINDATA            "HTML_FILE1_include_test.html"')
211    # hackety hack to work on win32&lin
212    output = re.sub('"[c-zC-Z]:', '"', output)
213    self.assertEqual(expected, output)
214
215    file_contents = util.ReadFile(output_file, util.RAW_TEXT)
216
217    # Check for the content added by the <include> tag.
218    self.failUnless(file_contents.find('Hello Include!') != -1)
219    # Check for the content that was removed by if tag.
220    self.failUnless(file_contents.find('should be removed') == -1)
221    # Check for the content that was kept in place by if.
222    self.failUnless(file_contents.find('should be kept') != -1)
223    self.failUnless(file_contents.find('in the middle...') != -1)
224    self.failUnless(file_contents.find('at the end...') != -1)
225    # Check for nested content that was kept
226    self.failUnless(file_contents.find('nested true should be kept') != -1)
227    self.failUnless(file_contents.find('silbing true should be kept') != -1)
228    # Check for removed "<if>" and "</if>" tags.
229    self.failUnless(file_contents.find('<if expr=') == -1)
230    self.failUnless(file_contents.find('</if>') == -1)
231
232
233  def testStructureNodeOutputfile(self):
234    input_file = util.PathFromRoot('grit/testdata/simple.html')
235    root = util.ParseGrdForUnittest('''\
236        <structures>
237          <structure type="tr_html" name="IDR_HTML" file="%s" />
238        </structures>''' % input_file)
239    struct, = root.GetChildrenOfType(structure.StructureNode)
240    # We must run the gatherer since we'll be wanting the translation of the
241    # file.  The file exists in the location pointed to.
242    root.SetOutputLanguage('en')
243    root.RunGatherers()
244
245    output_dir = tempfile.gettempdir()
246    en_file = struct.FileForLanguage('en', output_dir)
247    self.failUnless(en_file == input_file)
248    fr_file = struct.FileForLanguage('fr', output_dir)
249    self.failUnless(fr_file == os.path.join(output_dir, 'fr_simple.html'))
250
251    contents = util.ReadFile(fr_file, util.RAW_TEXT)
252
253    self.failUnless(contents.find('<p>') != -1)  # should contain the markup
254    self.failUnless(contents.find('Hello!') == -1)  # should be translated
255
256
257  def testChromeHtmlNodeOutputfile(self):
258    input_file = util.PathFromRoot('grit/testdata/chrome_html.html')
259    output_file = '%s/HTML_FILE1_chrome_html.html' % tempfile.gettempdir()
260    root = util.ParseGrdForUnittest('''\
261        <structures>
262          <structure type="chrome_html" name="HTML_FILE1" file="%s" flattenhtml="true" />
263        </structures>''' % input_file)
264    struct, = root.GetChildrenOfType(structure.StructureNode)
265    struct.gatherer.SetDefines({'scale_factors': '2x'})
266    # We must run the gatherers since we'll be wanting the chrome_html output.
267    # The file exists in the location pointed to.
268    root.SetOutputLanguage('en')
269    root.RunGatherers()
270
271    buf = StringIO.StringIO()
272    build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en', output_file),
273                                buf)
274    output = util.StripBlankLinesAndComments(buf.getvalue())
275    expected = (_PREAMBLE +
276        u'HTML_FILE1         BINDATA            "HTML_FILE1_chrome_html.html"')
277    # hackety hack to work on win32&lin
278    output = re.sub('"[c-zC-Z]:', '"', output)
279    self.assertEqual(expected, output)
280
281    file_contents = util.ReadFile(output_file, util.RAW_TEXT)
282
283    # Check for the content added by the <include> tag.
284    self.failUnless(file_contents.find('Hello Include!') != -1)
285    # Check for inserted -webkit-image-set.
286    self.failUnless(file_contents.find('content: -webkit-image-set') != -1)
287
288
289  def testSubstitutionHtml(self):
290    input_file = util.PathFromRoot('grit/testdata/toolbar_about.html')
291    root = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
292      <grit latest_public_release="2" source_lang_id="en-US" current_release="3" base_dir=".">
293        <release seq="1" allow_pseudo="False">
294          <structures fallback_to_english="True">
295            <structure type="tr_html" name="IDR_HTML" file="%s" expand_variables="true"/>
296          </structures>
297        </release>
298      </grit>
299      ''' % input_file), util.PathFromRoot('.'))
300    root.SetOutputLanguage('ar')
301    # We must run the gatherers since we'll be wanting the translation of the
302    # file.  The file exists in the location pointed to.
303    root.RunGatherers()
304
305    output_dir = tempfile.gettempdir()
306    struct, = root.GetChildrenOfType(structure.StructureNode)
307    ar_file = struct.FileForLanguage('ar', output_dir)
308    self.failUnless(ar_file == os.path.join(output_dir,
309                                            'ar_toolbar_about.html'))
310
311    contents = util.ReadFile(ar_file, util.RAW_TEXT)
312
313    self.failUnless(contents.find('dir="RTL"') != -1)
314
315
316  def testFallbackToEnglish(self):
317    root = util.ParseGrdForUnittest('''\
318        <structures fallback_to_english="True">
319          <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\testdata\klonk.rc" encoding="utf-16" />
320        </structures>''', base_dir=util.PathFromRoot('.'))
321    root.SetOutputLanguage('en')
322    root.RunGatherers()
323
324    buf = StringIO.StringIO()
325    formatter = build.RcBuilder.ProcessNode(
326        root, DummyOutput('rc_all', 'bingobongo'), buf)
327    output = util.StripBlankLinesAndComments(buf.getvalue())
328    self.assertEqual(_PREAMBLE + '''\
329IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
330STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
331CAPTION "About"
332FONT 8, "System", 0, 0, 0x0
333BEGIN
334    ICON            IDI_KLONK,IDC_MYICON,14,9,20,20
335    LTEXT           "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
336                    SS_NOPREFIX
337    LTEXT           "Copyright (C) 2005",IDC_STATIC,49,20,119,8
338    DEFPUSHBUTTON   "OK",IDOK,195,6,30,11,WS_GROUP
339    CONTROL         "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
340                    BS_AUTORADIOBUTTON,46,51,84,10
341END''', output)
342
343
344  def testSubstitutionRc(self):
345    root = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UTF-8"?>
346    <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
347          base_dir=".">
348      <outputs>
349        <output lang="en" type="rc_all" filename="grit\\testdata\klonk_resources.rc"/>
350      </outputs>
351      <release seq="1" allow_pseudo="False">
352        <structures>
353          <structure type="menu" name="IDC_KLONKMENU"
354              file="grit\\testdata\klonk.rc" encoding="utf-16"
355              expand_variables="true" />
356        </structures>
357        <messages>
358          <message name="good" sub_variable="true">
359            excellent
360          </message>
361        </messages>
362      </release>
363    </grit>
364    '''), util.PathFromRoot('.'))
365    root.SetOutputLanguage('en')
366    root.RunGatherers()
367
368    buf = StringIO.StringIO()
369    build.RcBuilder.ProcessNode(root, DummyOutput('rc_all', 'en'), buf)
370    output = buf.getvalue()
371    self.assertEqual('''
372// This file is automatically generated by GRIT.  Do not edit.
373
374#include "resource.h"
375#include <winresrc.h>
376#ifdef IDC_STATIC
377#undef IDC_STATIC
378#endif
379#define IDC_STATIC (-1)
380
381LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
382
383
384IDC_KLONKMENU MENU
385BEGIN
386    POPUP "&File"
387    BEGIN
388        MENUITEM "E&xit",                       IDM_EXIT
389        MENUITEM "This be ""Klonk"" me like",   ID_FILE_THISBE
390        POPUP "gonk"
391        BEGIN
392            MENUITEM "Klonk && is excellent",           ID_GONK_KLONKIS
393        END
394    END
395    POPUP "&Help"
396    BEGIN
397        MENUITEM "&About ...",                  IDM_ABOUT
398    END
399END
400
401STRINGTABLE
402BEGIN
403  good            "excellent"
404END
405'''.strip(), output.strip())
406
407
408if __name__ == '__main__':
409  unittest.main()
410