| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 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 misc.GritNode''' | |
| 7 | |
| 8 | |
| 9 import os | |
| 10 import sys | |
| 11 if __name__ == '__main__': | |
| 12 sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) | |
| 13 | |
| 14 import unittest | |
| 15 import StringIO | |
| 16 | |
| 17 from grit import grd_reader | |
| 18 import grit.exception | |
| 19 from grit import util | |
| 20 from grit.format import rc | |
| 21 from grit.node import misc | |
| 22 | |
| 23 | |
| 24 class GritNodeUnittest(unittest.TestCase): | |
| 25 def testUniqueNameAttribute(self): | |
| 26 try: | |
| 27 restree = grd_reader.Parse( | |
| 28 util.PathFromRoot('grit/testdata/duplicate-name-input.xml')) | |
| 29 self.fail('Expected parsing exception because of duplicate names.') | |
| 30 except grit.exception.Parsing: | |
| 31 pass # Expected case | |
| 32 | |
| 33 def testReadFirstIdsFromFile(self): | |
| 34 test_resource_ids = os.path.join(os.path.dirname(__file__), '..', | |
| 35 'testdata', 'resource_ids') | |
| 36 base_dir = os.path.dirname(test_resource_ids) | |
| 37 | |
| 38 src_dir, id_dict = misc._ReadFirstIdsFromFile( | |
| 39 test_resource_ids, | |
| 40 { | |
| 41 'FOO': os.path.join(base_dir, 'bar'), | |
| 42 'SHARED_INTERMEDIATE_DIR': os.path.join(base_dir, | |
| 43 'out/Release/obj/gen'), | |
| 44 }) | |
| 45 self.assertEqual({}, id_dict.get('bar/file.grd', None)) | |
| 46 self.assertEqual({}, | |
| 47 id_dict.get('out/Release/obj/gen/devtools/devtools.grd', None)) | |
| 48 | |
| 49 src_dir, id_dict = misc._ReadFirstIdsFromFile( | |
| 50 test_resource_ids, | |
| 51 { | |
| 52 'SHARED_INTERMEDIATE_DIR': '/outside/src_dir', | |
| 53 }) | |
| 54 self.assertEqual({}, id_dict.get('devtools.grd', None)) | |
| 55 | |
| 56 # Verifies that GetInputFiles() returns the correct list of files | |
| 57 # corresponding to ChromeScaledImage nodes when assets are missing. | |
| 58 def testGetInputFilesChromeScaledImage(self): | |
| 59 chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html') | |
| 60 xml = '''<?xml version="1.0" encoding="utf-8"?> | |
| 61 <grit latest_public_release="0" current_release="1"> | |
| 62 <outputs> | |
| 63 <output filename="default.pak" type="data_package" context="default_10
0_percent" /> | |
| 64 <output filename="special.pak" type="data_package" context="special_10
0_percent" fallback_to_default_layout="false" /> | |
| 65 </outputs> | |
| 66 <release seq="1"> | |
| 67 <structures fallback_to_low_resolution="true"> | |
| 68 <structure type="chrome_scaled_image" name="IDR_A" file="a.png" /> | |
| 69 <structure type="chrome_scaled_image" name="IDR_B" file="b.png" /> | |
| 70 <structure type="chrome_html" name="HTML_FILE1" file="%s" flattenhtm
l="true" /> | |
| 71 </structures> | |
| 72 </release> | |
| 73 </grit>''' % chrome_html_path | |
| 74 | |
| 75 grd = grd_reader.Parse(StringIO.StringIO(xml), util.PathFromRoot('grit/testd
ata')) | |
| 76 expected = ['chrome_html.html', 'default_100_percent/a.png', | |
| 77 'default_100_percent/b.png', 'included_sample.html', | |
| 78 'special_100_percent/a.png'] | |
| 79 actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for path
in grd.GetInputFiles()] | |
| 80 # Convert path separator for Windows paths. | |
| 81 actual = [path.replace('\\', '/') for path in actual] | |
| 82 self.assertEquals(expected, actual) | |
| 83 | |
| 84 # Verifies that GetInputFiles() returns the correct list of files | |
| 85 # when files include other files. | |
| 86 def testGetInputFilesFromIncludes(self): | |
| 87 chrome_html_path = util.PathFromRoot('grit/testdata/chrome_html.html') | |
| 88 xml = '''<?xml version="1.0" encoding="utf-8"?> | |
| 89 <grit latest_public_release="0" current_release="1"> | |
| 90 <outputs> | |
| 91 <output filename="default.pak" type="data_package" context="default_10
0_percent" /> | |
| 92 <output filename="special.pak" type="data_package" context="special_10
0_percent" fallback_to_default_layout="false" /> | |
| 93 </outputs> | |
| 94 <release seq="1"> | |
| 95 <includes> | |
| 96 <include name="IDR_TESTDATA_CHROME_HTML" file="%s" flattenhtml="true
" | |
| 97 allowexternalscript="true" type="BINDATA" /> | |
| 98 </includes> | |
| 99 </release> | |
| 100 </grit>''' % chrome_html_path | |
| 101 | |
| 102 grd = grd_reader.Parse(StringIO.StringIO(xml), util.PathFromRoot('grit/testd
ata')) | |
| 103 expected = ['chrome_html.html', 'included_sample.html'] | |
| 104 actual = [os.path.relpath(path, util.PathFromRoot('grit/testdata')) for path
in grd.GetInputFiles()] | |
| 105 # Convert path separator for Windows paths. | |
| 106 actual = [path.replace('\\', '/') for path in actual] | |
| 107 self.assertEquals(expected, actual) | |
| 108 | |
| 109 | |
| 110 class IfNodeUnittest(unittest.TestCase): | |
| 111 def testIffyness(self): | |
| 112 grd = grd_reader.Parse(StringIO.StringIO(''' | |
| 113 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 114 <release seq="3"> | |
| 115 <messages> | |
| 116 <if expr="'bingo' in defs"> | |
| 117 <message name="IDS_BINGO"> | |
| 118 Bingo! | |
| 119 </message> | |
| 120 </if> | |
| 121 <if expr="'hello' in defs"> | |
| 122 <message name="IDS_HELLO"> | |
| 123 Hello! | |
| 124 </message> | |
| 125 </if> | |
| 126 <if expr="lang == 'fr' or 'FORCE_FRENCH' in defs"> | |
| 127 <message name="IDS_HELLO" internal_comment="French version"> | |
| 128 Good morning | |
| 129 </message> | |
| 130 </if> | |
| 131 <if expr="is_win"> | |
| 132 <message name="IDS_ISWIN">is_win</message> | |
| 133 </if> | |
| 134 </messages> | |
| 135 </release> | |
| 136 </grit>'''), dir='.') | |
| 137 | |
| 138 messages_node = grd.children[0].children[0] | |
| 139 bingo_message = messages_node.children[0].children[0] | |
| 140 hello_message = messages_node.children[1].children[0] | |
| 141 french_message = messages_node.children[2].children[0] | |
| 142 is_win_message = messages_node.children[3].children[0] | |
| 143 | |
| 144 self.assertTrue(bingo_message.name == 'message') | |
| 145 self.assertTrue(hello_message.name == 'message') | |
| 146 self.assertTrue(french_message.name == 'message') | |
| 147 | |
| 148 grd.SetOutputLanguage('fr') | |
| 149 grd.SetDefines({'hello': '1'}) | |
| 150 active = set(grd.ActiveDescendants()) | |
| 151 self.failUnless(bingo_message not in active) | |
| 152 self.failUnless(hello_message in active) | |
| 153 self.failUnless(french_message in active) | |
| 154 | |
| 155 grd.SetOutputLanguage('en') | |
| 156 grd.SetDefines({'bingo': 1}) | |
| 157 active = set(grd.ActiveDescendants()) | |
| 158 self.failUnless(bingo_message in active) | |
| 159 self.failUnless(hello_message not in active) | |
| 160 self.failUnless(french_message not in active) | |
| 161 | |
| 162 grd.SetOutputLanguage('en') | |
| 163 grd.SetDefines({'FORCE_FRENCH': '1', 'bingo': '1'}) | |
| 164 active = set(grd.ActiveDescendants()) | |
| 165 self.failUnless(bingo_message in active) | |
| 166 self.failUnless(hello_message not in active) | |
| 167 self.failUnless(french_message in active) | |
| 168 | |
| 169 grd.SetOutputLanguage('en') | |
| 170 grd.SetDefines({}) | |
| 171 self.failUnless(grd.target_platform == sys.platform) | |
| 172 grd.SetTargetPlatform('darwin') | |
| 173 active = set(grd.ActiveDescendants()) | |
| 174 self.failUnless(is_win_message not in active) | |
| 175 grd.SetTargetPlatform('win32') | |
| 176 active = set(grd.ActiveDescendants()) | |
| 177 self.failUnless(is_win_message in active) | |
| 178 | |
| 179 def testElsiness(self): | |
| 180 grd = util.ParseGrdForUnittest(''' | |
| 181 <messages> | |
| 182 <if expr="True"> | |
| 183 <then> <message name="IDS_YES1"></message> </then> | |
| 184 <else> <message name="IDS_NO1"></message> </else> | |
| 185 </if> | |
| 186 <if expr="True"> | |
| 187 <then> <message name="IDS_YES2"></message> </then> | |
| 188 <else> </else> | |
| 189 </if> | |
| 190 <if expr="True"> | |
| 191 <then> </then> | |
| 192 <else> <message name="IDS_NO2"></message> </else> | |
| 193 </if> | |
| 194 <if expr="True"> | |
| 195 <then> </then> | |
| 196 <else> </else> | |
| 197 </if> | |
| 198 <if expr="False"> | |
| 199 <then> <message name="IDS_NO3"></message> </then> | |
| 200 <else> <message name="IDS_YES3"></message> </else> | |
| 201 </if> | |
| 202 <if expr="False"> | |
| 203 <then> <message name="IDS_NO4"></message> </then> | |
| 204 <else> </else> | |
| 205 </if> | |
| 206 <if expr="False"> | |
| 207 <then> </then> | |
| 208 <else> <message name="IDS_YES4"></message> </else> | |
| 209 </if> | |
| 210 <if expr="False"> | |
| 211 <then> </then> | |
| 212 <else> </else> | |
| 213 </if> | |
| 214 </messages>''') | |
| 215 included = [msg.attrs['name'] for msg in grd.ActiveDescendants() | |
| 216 if msg.name == 'message'] | |
| 217 self.assertEqual(['IDS_YES1', 'IDS_YES2', 'IDS_YES3', 'IDS_YES4'], included) | |
| 218 | |
| 219 def testIffynessWithOutputNodes(self): | |
| 220 grd = grd_reader.Parse(StringIO.StringIO(''' | |
| 221 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 222 <outputs> | |
| 223 <output filename="uncond1.rc" type="rc_data" /> | |
| 224 <if expr="lang == 'fr' or 'hello' in defs"> | |
| 225 <output filename="only_fr.adm" type="adm" /> | |
| 226 <output filename="only_fr.plist" type="plist" /> | |
| 227 </if> | |
| 228 <if expr="lang == 'ru'"> | |
| 229 <output filename="doc.html" type="document" /> | |
| 230 </if> | |
| 231 <output filename="uncond2.adm" type="adm" /> | |
| 232 <output filename="iftest.h" type="rc_header"> | |
| 233 <emit emit_type='prepend'></emit> | |
| 234 </output> | |
| 235 </outputs> | |
| 236 </grit>'''), dir='.') | |
| 237 | |
| 238 outputs_node = grd.children[0] | |
| 239 uncond1_output = outputs_node.children[0] | |
| 240 only_fr_adm_output = outputs_node.children[1].children[0] | |
| 241 only_fr_plist_output = outputs_node.children[1].children[1] | |
| 242 doc_output = outputs_node.children[2].children[0] | |
| 243 uncond2_output = outputs_node.children[0] | |
| 244 self.assertTrue(uncond1_output.name == 'output') | |
| 245 self.assertTrue(only_fr_adm_output.name == 'output') | |
| 246 self.assertTrue(only_fr_plist_output.name == 'output') | |
| 247 self.assertTrue(doc_output.name == 'output') | |
| 248 self.assertTrue(uncond2_output.name == 'output') | |
| 249 | |
| 250 grd.SetOutputLanguage('ru') | |
| 251 grd.SetDefines({'hello': '1'}) | |
| 252 outputs = [output.GetFilename() for output in grd.GetOutputFiles()] | |
| 253 self.assertEquals( | |
| 254 outputs, | |
| 255 ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'doc.html', | |
| 256 'uncond2.adm', 'iftest.h']) | |
| 257 | |
| 258 grd.SetOutputLanguage('ru') | |
| 259 grd.SetDefines({'bingo': '2'}) | |
| 260 outputs = [output.GetFilename() for output in grd.GetOutputFiles()] | |
| 261 self.assertEquals( | |
| 262 outputs, | |
| 263 ['uncond1.rc', 'doc.html', 'uncond2.adm', 'iftest.h']) | |
| 264 | |
| 265 grd.SetOutputLanguage('fr') | |
| 266 grd.SetDefines({'hello': '1'}) | |
| 267 outputs = [output.GetFilename() for output in grd.GetOutputFiles()] | |
| 268 self.assertEquals( | |
| 269 outputs, | |
| 270 ['uncond1.rc', 'only_fr.adm', 'only_fr.plist', 'uncond2.adm', | |
| 271 'iftest.h']) | |
| 272 | |
| 273 grd.SetOutputLanguage('en') | |
| 274 grd.SetDefines({'bingo': '1'}) | |
| 275 outputs = [output.GetFilename() for output in grd.GetOutputFiles()] | |
| 276 self.assertEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h']) | |
| 277 | |
| 278 grd.SetOutputLanguage('fr') | |
| 279 grd.SetDefines({'bingo': '1'}) | |
| 280 outputs = [output.GetFilename() for output in grd.GetOutputFiles()] | |
| 281 self.assertNotEquals(outputs, ['uncond1.rc', 'uncond2.adm', 'iftest.h']) | |
| 282 | |
| 283 def testChildrenAccepted(self): | |
| 284 grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0"?> | |
| 285 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 286 <release seq="3"> | |
| 287 <includes> | |
| 288 <if expr="'bingo' in defs"> | |
| 289 <include type="gif" name="ID_LOGO2" file="images/logo2.gif" /> | |
| 290 </if> | |
| 291 <if expr="'bingo' in defs"> | |
| 292 <if expr="'hello' in defs"> | |
| 293 <include type="gif" name="ID_LOGO2" file="images/logo2.gif" /> | |
| 294 </if> | |
| 295 </if> | |
| 296 </includes> | |
| 297 <structures> | |
| 298 <if expr="'bingo' in defs"> | |
| 299 <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\test\data
\klonk.rc" encoding="utf-16" /> | |
| 300 </if> | |
| 301 <if expr="'bingo' in defs"> | |
| 302 <if expr="'hello' in defs"> | |
| 303 <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\test\da
ta\klonk.rc" encoding="utf-16" /> | |
| 304 </if> | |
| 305 </if> | |
| 306 </structures> | |
| 307 <messages> | |
| 308 <if expr="'bingo' in defs"> | |
| 309 <message name="IDS_BINGO">Bingo!</message> | |
| 310 </if> | |
| 311 <if expr="'bingo' in defs"> | |
| 312 <if expr="'hello' in defs"> | |
| 313 <message name="IDS_BINGO">Bingo!</message> | |
| 314 </if> | |
| 315 </if> | |
| 316 </messages> | |
| 317 </release> | |
| 318 <translations> | |
| 319 <if expr="'bingo' in defs"> | |
| 320 <file lang="nl" path="nl_translations.xtb" /> | |
| 321 </if> | |
| 322 <if expr="'bingo' in defs"> | |
| 323 <if expr="'hello' in defs"> | |
| 324 <file lang="nl" path="nl_translations.xtb" /> | |
| 325 </if> | |
| 326 </if> | |
| 327 </translations> | |
| 328 </grit>'''), dir='.') | |
| 329 | |
| 330 def testIfBadChildrenNesting(self): | |
| 331 # includes | |
| 332 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 333 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 334 <release seq="3"> | |
| 335 <includes> | |
| 336 <if expr="'bingo' in defs"> | |
| 337 <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\test\data
\klonk.rc" encoding="utf-16" /> | |
| 338 </if> | |
| 339 </includes> | |
| 340 </release> | |
| 341 </grit>''') | |
| 342 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 343 # messages | |
| 344 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 345 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 346 <release seq="3"> | |
| 347 <messages> | |
| 348 <if expr="'bingo' in defs"> | |
| 349 <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\test\data
\klonk.rc" encoding="utf-16" /> | |
| 350 </if> | |
| 351 </messages> | |
| 352 </release> | |
| 353 </grit>''') | |
| 354 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 355 # structures | |
| 356 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 357 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 358 <release seq="3"> | |
| 359 <structures> | |
| 360 <if expr="'bingo' in defs"> | |
| 361 <message name="IDS_BINGO">Bingo!</message> | |
| 362 </if> | |
| 363 </structures> | |
| 364 </release> | |
| 365 </grit>''') | |
| 366 # translations | |
| 367 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 368 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 369 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 370 <translations> | |
| 371 <if expr="'bingo' in defs"> | |
| 372 <message name="IDS_BINGO">Bingo!</message> | |
| 373 </if> | |
| 374 </translations> | |
| 375 </grit>''') | |
| 376 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 377 # same with nesting | |
| 378 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 379 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 380 <release seq="3"> | |
| 381 <includes> | |
| 382 <if expr="'bingo' in defs"> | |
| 383 <if expr="'hello' in defs"> | |
| 384 <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\test\da
ta\klonk.rc" encoding="utf-16" /> | |
| 385 </if> | |
| 386 </if> | |
| 387 </includes> | |
| 388 </release> | |
| 389 </grit>''') | |
| 390 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 391 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 392 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 393 <release seq="3"> | |
| 394 <messages> | |
| 395 <if expr="'bingo' in defs"> | |
| 396 <if expr="'hello' in defs"> | |
| 397 <structure type="dialog" name="IDD_ABOUTBOX" file="grit\\test\da
ta\klonk.rc" encoding="utf-16" /> | |
| 398 </if> | |
| 399 </if> | |
| 400 </messages> | |
| 401 </release> | |
| 402 </grit>''') | |
| 403 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 404 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 405 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 406 <release seq="3"> | |
| 407 <structures> | |
| 408 <if expr="'bingo' in defs"> | |
| 409 <if expr="'hello' in defs"> | |
| 410 <message name="IDS_BINGO">Bingo!</message> | |
| 411 </if> | |
| 412 </if> | |
| 413 </structures> | |
| 414 </release> | |
| 415 </grit>''') | |
| 416 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 417 xml = StringIO.StringIO('''<?xml version="1.0"?> | |
| 418 <grit latest_public_release="2" source_lang_id="en-US" current_release="3"
base_dir="."> | |
| 419 <translations> | |
| 420 <if expr="'bingo' in defs"> | |
| 421 <if expr="'hello' in defs"> | |
| 422 <message name="IDS_BINGO">Bingo!</message> | |
| 423 </if> | |
| 424 </if> | |
| 425 </translations> | |
| 426 </grit>''') | |
| 427 self.assertRaises(grit.exception.UnexpectedChild, grd_reader.Parse, xml) | |
| 428 | |
| 429 | |
| 430 class ReleaseNodeUnittest(unittest.TestCase): | |
| 431 def testPseudoControl(self): | |
| 432 grd = grd_reader.Parse(StringIO.StringIO('''<?xml version="1.0" encoding="UT
F-8"?> | |
| 433 <grit latest_public_release="1" source_lang_id="en-US" current_release="2"
base_dir="."> | |
| 434 <release seq="1" allow_pseudo="false"> | |
| 435 <messages> | |
| 436 <message name="IDS_HELLO"> | |
| 437 Hello | |
| 438 </message> | |
| 439 </messages> | |
| 440 <structures> | |
| 441 <structure type="dialog" name="IDD_ABOUTBOX" encoding="utf-16" file=
"klonk.rc" /> | |
| 442 </structures> | |
| 443 </release> | |
| 444 <release seq="2"> | |
| 445 <messages> | |
| 446 <message name="IDS_BINGO"> | |
| 447 Bingo | |
| 448 </message> | |
| 449 </messages> | |
| 450 <structures> | |
| 451 <structure type="menu" name="IDC_KLONKMENU" encoding="utf-16" file="
klonk.rc" /> | |
| 452 </structures> | |
| 453 </release> | |
| 454 </grit>'''), util.PathFromRoot('grit/testdata')) | |
| 455 grd.SetOutputLanguage('en') | |
| 456 grd.RunGatherers() | |
| 457 | |
| 458 hello = grd.GetNodeById('IDS_HELLO') | |
| 459 aboutbox = grd.GetNodeById('IDD_ABOUTBOX') | |
| 460 bingo = grd.GetNodeById('IDS_BINGO') | |
| 461 menu = grd.GetNodeById('IDC_KLONKMENU') | |
| 462 | |
| 463 for node in [hello, aboutbox]: | |
| 464 self.failUnless(not node.PseudoIsAllowed()) | |
| 465 | |
| 466 for node in [bingo, menu]: | |
| 467 self.failUnless(node.PseudoIsAllowed()) | |
| 468 | |
| 469 # TODO(benrg): There was a test here that formatting hello and aboutbox with | |
| 470 # a pseudo language should fail, but they do not fail and the test was | |
| 471 # broken and failed to catch it. Fix this. | |
| 472 | |
| 473 # Should not raise an exception since pseudo is allowed | |
| 474 rc.FormatMessage(bingo, 'xyz-pseudo') | |
| 475 rc.FormatStructure(menu, 'xyz-pseudo', '.') | |
| 476 | |
| 477 | |
| 478 if __name__ == '__main__': | |
| 479 unittest.main() | |
| OLD | NEW |