• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2014 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Tests for enum_preprocess.py.
7
8This test suite contains various tests for the C++ -> Java enum generator.
9"""
10
11import collections
12from datetime import date
13import unittest
14
15import java_cpp_enum
16from java_cpp_enum import EnumDefinition, GenerateOutput
17from java_cpp_enum import HeaderParser
18from util import java_cpp_utils
19
20
21class TestPreprocess(unittest.TestCase):
22  def testOutput(self):
23    definition = EnumDefinition(original_enum_name='ClassName',
24                                enum_package='some.package',
25                                entries=[('E1', 1), ('E2', '2 << 2')],
26                                comments=[('E2', 'This is a comment.'),
27                                          ('E1', 'This is a multiple line '
28                                                 'comment that is really long. '
29                                                 'This is a multiple line '
30                                                 'comment that is really '
31                                                 'really long.')])
32    output = GenerateOutput('path/to/file', definition)
33    expected = """
34// Copyright %d The Chromium Authors
35// Use of this source code is governed by a BSD-style license that can be
36// found in the LICENSE file.
37
38// This file is autogenerated by
39//     %s
40// From
41//     path/to/file
42
43package some.package;
44
45import androidx.annotation.IntDef;
46
47import java.lang.annotation.Retention;
48import java.lang.annotation.RetentionPolicy;
49
50@IntDef({
51    ClassName.E1, ClassName.E2
52})
53@Retention(RetentionPolicy.SOURCE)
54public @interface ClassName {
55  /**
56   * %s
57   * really really long.
58   */
59  int E1 = 1;
60  /**
61   * This is a comment.
62   */
63  int E2 = 2 << 2;
64}
65"""
66    long_comment = ('This is a multiple line comment that is really long. '
67                    'This is a multiple line comment that is')
68    self.assertEqual(
69        expected % (date.today().year, java_cpp_utils.GetScriptName(),
70                    long_comment), output)
71
72  def testParseSimpleEnum(self):
73    test_data = """
74      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
75      enum EnumName {
76        VALUE_ZERO,
77        VALUE_ONE,
78      };
79    """.split('\n')
80    definitions = HeaderParser(test_data).ParseDefinitions()
81    self.assertEqual(1, len(definitions))
82    definition = definitions[0]
83    self.assertEqual('EnumName', definition.class_name)
84    self.assertEqual('test.namespace', definition.enum_package)
85    self.assertEqual(collections.OrderedDict([('VALUE_ZERO', 0),
86                                              ('VALUE_ONE', 1)]),
87                     definition.entries)
88
89  def testOutputFlag(self):
90    for [attr, want_flag] in [
91        ['0', False],
92        ['1', True],
93        ['false', False],
94        ['true', True],
95    ]:
96      test_data = ("""
97        // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
98        // GENERATED_JAVA_IS_FLAG: %s
99        enum EnumName {
100          ZERO = 1 << 0,
101          ONE = 1 << 1,
102        };
103      """ % attr).split('\n')
104      definitions = HeaderParser(test_data).ParseDefinitions()
105      output = GenerateOutput('/path/to/file', definitions[0])
106      int_def = output[output.index("@IntDef"):]
107      expected = """@IntDef(%s{
108    EnumName.ZERO, EnumName.ONE
109})
110@Retention(RetentionPolicy.SOURCE)
111public @interface EnumName {
112  int ZERO = 1 << 0;
113  int ONE = 1 << 1;
114}
115""" % ('flag = true, value = ' if want_flag else '')
116      self.assertEqual(int_def, expected)
117
118  def testParseBitShifts(self):
119    test_data = """
120      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
121      enum EnumName {
122        VALUE_ZERO = 1 << 0,
123        VALUE_ONE = 1 << 1,
124      };
125
126      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
127      enum EnumName {
128        ENUM_NAME_ZERO = 1 << 0,
129        ENUM_NAME_ONE = 1 << 1,
130        ENUM_NAME_TWO = ENUM_NAME_ZERO | ENUM_NAME_ONE,
131      };
132    """.split('\n')
133    definitions = HeaderParser(test_data).ParseDefinitions()
134    self.assertEqual(2, len(definitions))
135    definition = definitions[0]
136    self.assertEqual('EnumName', definition.class_name)
137    self.assertEqual('test.namespace', definition.enum_package)
138    self.assertEqual(collections.OrderedDict([('VALUE_ZERO', '1 << 0'),
139                                              ('VALUE_ONE', '1 << 1')]),
140                     definition.entries)
141
142    definition = definitions[1]
143    expected_entries = collections.OrderedDict([
144        ('ZERO', '1 << 0'),
145        ('ONE', '1 << 1'),
146        ('TWO', 'ZERO | ONE')])
147    self.assertEqual(expected_entries, definition.entries)
148
149  def testParseMultilineEnumEntry(self):
150    test_data = """
151      // GENERATED_JAVA_ENUM_PACKAGE: bar.namespace
152      enum Foo {
153        VALUE_ZERO = 1 << 0,
154        VALUE_ONE =
155            SymbolKey | FnKey | AltGrKey | MetaKey | AltKey | ControlKey,
156        VALUE_TWO = 1 << 18,
157      };
158    """.split('\n')
159    expected_entries = collections.OrderedDict([
160        ('VALUE_ZERO', '1 << 0'),
161        ('VALUE_ONE', 'SymbolKey | FnKey | AltGrKey | MetaKey | AltKey | '
162         'ControlKey'),
163        ('VALUE_TWO', '1 << 18')])
164    definitions = HeaderParser(test_data).ParseDefinitions()
165    self.assertEqual(1, len(definitions))
166    definition = definitions[0]
167    self.assertEqual('Foo', definition.class_name)
168    self.assertEqual('bar.namespace', definition.enum_package)
169    self.assertEqual(expected_entries, definition.entries)
170
171  def testParseEnumEntryWithTrailingMultilineEntry(self):
172    test_data = """
173      // GENERATED_JAVA_ENUM_PACKAGE: bar.namespace
174      enum Foo {
175        VALUE_ZERO = 1,
176        VALUE_ONE =
177            SymbolKey | FnKey | AltGrKey | MetaKey |
178            AltKey | ControlKey | ShiftKey,
179      };
180    """.split('\n')
181    expected_entries = collections.OrderedDict([
182        ('VALUE_ZERO', '1'),
183        ('VALUE_ONE', 'SymbolKey | FnKey | AltGrKey | MetaKey | AltKey | '
184         'ControlKey | ShiftKey')])
185    definitions = HeaderParser(test_data).ParseDefinitions()
186    self.assertEqual(1, len(definitions))
187    definition = definitions[0]
188    self.assertEqual('Foo', definition.class_name)
189    self.assertEqual('bar.namespace', definition.enum_package)
190    self.assertEqual(expected_entries, definition.entries)
191
192  def testParseNoCommaAfterLastEntry(self):
193    test_data = """
194      // GENERATED_JAVA_ENUM_PACKAGE: bar.namespace
195      enum Foo {
196        VALUE_ZERO = 1,
197
198        // This is a multiline
199        //
200        // comment with an empty line.
201        VALUE_ONE = 2
202      };
203    """.split('\n')
204    expected_entries = collections.OrderedDict([
205        ('VALUE_ZERO', '1'),
206        ('VALUE_ONE', '2')])
207    expected_comments = collections.OrderedDict([
208        ('VALUE_ONE', 'This is a multiline comment with an empty line.')])
209    definitions = HeaderParser(test_data).ParseDefinitions()
210    self.assertEqual(1, len(definitions))
211    definition = definitions[0]
212    self.assertEqual('Foo', definition.class_name)
213    self.assertEqual('bar.namespace', definition.enum_package)
214    self.assertEqual(expected_entries, definition.entries)
215    self.assertEqual(expected_comments, definition.comments)
216
217  def testParseClassNameOverride(self):
218    test_data = """
219      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
220      // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OverrideName
221      enum EnumName {
222        FOO
223      };
224
225      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
226      // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OtherOverride
227      enum PrefixTest {
228        PREFIX_TEST_A,
229        PREFIX_TEST_B,
230      };
231    """.split('\n')
232    definitions = HeaderParser(test_data).ParseDefinitions()
233    self.assertEqual(2, len(definitions))
234    definition = definitions[0]
235    self.assertEqual('OverrideName', definition.class_name)
236
237    definition = definitions[1]
238    self.assertEqual('OtherOverride', definition.class_name)
239    self.assertEqual(collections.OrderedDict([('A', 0),
240                                              ('B', 1)]),
241                     definition.entries)
242
243  def testParsePreservesCommentsWhenPrefixStripping(self):
244    test_data = """
245      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
246      enum EnumOne {
247        ENUM_ONE_A = 1,
248        // Comment there
249        ENUM_ONE_B = A,
250      };
251
252      enum EnumIgnore {
253        C, D, E
254      };
255
256      // GENERATED_JAVA_ENUM_PACKAGE: other.package
257      // GENERATED_JAVA_PREFIX_TO_STRIP: P_
258      enum EnumTwo {
259        P_A,
260        // This comment spans
261        // two lines.
262        P_B
263      };
264    """.split('\n')
265    definitions = HeaderParser(test_data).ParseDefinitions()
266    self.assertEqual(2, len(definitions))
267    definition = definitions[0]
268    self.assertEqual('EnumOne', definition.class_name)
269    self.assertEqual('test.namespace', definition.enum_package)
270    self.assertEqual(collections.OrderedDict([('A', '1'),
271                                              ('B', 'A')]),
272                     definition.entries)
273    self.assertEqual(collections.OrderedDict([('B', 'Comment there')]),
274                     definition.comments)
275    definition = definitions[1]
276    self.assertEqual('EnumTwo', definition.class_name)
277    self.assertEqual('other.package', definition.enum_package)
278    self.assertEqual(collections.OrderedDict(
279        [('B', 'This comment spans two lines.')]), definition.comments)
280    self.assertEqual(collections.OrderedDict([('A', 0),
281                                              ('B', 1)]),
282                     definition.entries)
283
284  def testParseTwoEnums(self):
285    test_data = """
286      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
287      enum AnEnum {
288        ENUM_ONE_A = 1,
289        ENUM_ONE_B = A,
290      };
291
292      enum EnumIgnore {
293        C, D, E
294      };
295
296      // GENERATED_JAVA_ENUM_PACKAGE: other.package
297      enum EnumTwo {
298        P_A,
299        P_B
300      };
301    """.split('\n')
302    definitions = HeaderParser(test_data).ParseDefinitions()
303    self.assertEqual(2, len(definitions))
304    definition = definitions[0]
305    self.assertEqual('AnEnum', definition.class_name)
306    self.assertEqual('test.namespace', definition.enum_package)
307    self.assertEqual(collections.OrderedDict([('ENUM_ONE_A', '1'),
308                                              ('ENUM_ONE_B', 'A')]),
309                     definition.entries)
310    definition = definitions[1]
311    self.assertEqual('EnumTwo', definition.class_name)
312    self.assertEqual('other.package', definition.enum_package)
313    self.assertEqual(collections.OrderedDict([('P_A', 0),
314                                              ('P_B', 1)]),
315                     definition.entries)
316
317  def testParseSingleLineEnum(self):
318    test_data = """
319      // GENERATED_JAVA_ENUM_PACKAGE: other.package
320      // GENERATED_JAVA_PREFIX_TO_STRIP: P_
321      enum EnumTwo { P_A, P_B };
322    """.split('\n')
323    definitions = HeaderParser(test_data).ParseDefinitions()
324    definition = definitions[0]
325    self.assertEqual('EnumTwo', definition.class_name)
326    self.assertEqual('other.package', definition.enum_package)
327    self.assertEqual(collections.OrderedDict([('A', 0),
328                                              ('B', 1)]),
329                     definition.entries)
330
331  def testParseWithStrippingAndRelativeReferences(self):
332    test_data = """
333      // GENERATED_JAVA_ENUM_PACKAGE: other.package
334      // GENERATED_JAVA_PREFIX_TO_STRIP: P_
335      enum EnumTwo {
336        P_A = 1,
337        // P_A is old-don't use P_A.
338        P_B = P_A,
339      };
340    """.split('\n')
341    definitions = HeaderParser(test_data).ParseDefinitions()
342    definition = definitions[0]
343    self.assertEqual('EnumTwo', definition.class_name)
344    self.assertEqual('other.package', definition.enum_package)
345    self.assertEqual(collections.OrderedDict([('A', '1'),
346                                              ('B', 'A')]),
347                     definition.entries)
348    self.assertEqual(collections.OrderedDict([('B', 'A is old-don\'t use A.')]),
349                     definition.comments)
350
351  def testParseSingleLineAndRegularEnum(self):
352    test_data = """
353      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
354      enum EnumOne {
355        ENUM_ONE_A = 1,
356        // Comment there
357        ENUM_ONE_B = A,
358      };
359
360      // GENERATED_JAVA_ENUM_PACKAGE: other.package
361      enum EnumTwo { P_A, P_B };
362
363      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
364      // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OverrideName
365      enum EnumName {
366        ENUM_NAME_FOO
367      };
368    """.split('\n')
369    definitions = HeaderParser(test_data).ParseDefinitions()
370    definition = definitions[0]
371    self.assertEqual(
372        collections.OrderedDict([('A', '1'), ('B', 'A')]), definition.entries)
373    self.assertEqual(collections.OrderedDict([('B', 'Comment there')]),
374                     definition.comments)
375
376    self.assertEqual(3, len(definitions))
377    definition = definitions[1]
378    self.assertEqual(
379        collections.OrderedDict([('P_A', 0), ('P_B', 1)]), definition.entries)
380
381    definition = definitions[2]
382    self.assertEqual(collections.OrderedDict([('FOO', 0)]), definition.entries)
383
384  def testParseWithCamelCaseNames(self):
385    test_data = """
386      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
387      enum EnumTest {
388        EnumTestA = 1,
389        // comment for EnumTestB.
390        EnumTestB = 2,
391      };
392
393      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
394      // GENERATED_JAVA_PREFIX_TO_STRIP: Test
395      enum AnEnum {
396        TestHTTPOption,
397        TestHTTPSOption,
398      };
399
400    """.split('\n')
401    definitions = HeaderParser(test_data).ParseDefinitions()
402    definition = definitions[0]
403    self.assertEqual(
404        collections.OrderedDict([('A', '1'), ('B', '2')]),
405        definition.entries)
406    self.assertEqual(
407        collections.OrderedDict([('B', 'comment for B.')]),
408        definition.comments)
409
410    definition = definitions[1]
411    self.assertEqual(
412        collections.OrderedDict([('HTTP_OPTION', 0), ('HTTPS_OPTION', 1)]),
413        definition.entries)
414
415  def testParseWithKCamelCaseNames(self):
416    test_data = """
417      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
418      enum EnumOne {
419        kEnumOne = 1,
420        // comment for kEnumTwo.
421        kEnumTwo = 2,
422      };
423
424      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
425      // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OverrideName
426      enum EnumName {
427        kEnumNameFoo,
428        kEnumNameBar
429      };
430
431      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
432      enum EnumName {
433        kEnumNameFoo,
434        kEnumBar,
435      };
436
437      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
438      enum Keys {
439        kSymbolKey = 1 << 0,
440        kAltKey = 1 << 1,
441        kUpKey = 1 << 2,
442        kKeyModifiers = kSymbolKey | kAltKey | kUpKey | kKeyModifiers,
443      };
444
445      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
446      enum Mixed {
447        kTestVal,
448        kCodecMPEG2
449      };
450    """.split('\n')
451    definitions = HeaderParser(test_data).ParseDefinitions()
452    definition = definitions[0]
453    self.assertEqual(
454        collections.OrderedDict([('ENUM_ONE', '1'), ('ENUM_TWO', '2')]),
455        definition.entries)
456    self.assertEqual(
457        collections.OrderedDict([('ENUM_TWO', 'comment for ENUM_TWO.')]),
458        definition.comments)
459
460    definition = definitions[1]
461    self.assertEqual(
462        collections.OrderedDict([('FOO', 0), ('BAR', 1)]),
463        definition.entries)
464
465    definition = definitions[2]
466    self.assertEqual(
467        collections.OrderedDict([('ENUM_NAME_FOO', 0), ('ENUM_BAR', 1)]),
468        definition.entries)
469
470    definition = definitions[3]
471    expected_entries = collections.OrderedDict([
472        ('SYMBOL_KEY', '1 << 0'),
473        ('ALT_KEY', '1 << 1'),
474        ('UP_KEY', '1 << 2'),
475        ('KEY_MODIFIERS', 'SYMBOL_KEY | ALT_KEY | UP_KEY | KEY_MODIFIERS')])
476    self.assertEqual(expected_entries, definition.entries)
477
478    definition = definitions[4]
479    self.assertEqual(
480        collections.OrderedDict([('TEST_VAL', 0), ('CODEC_MPEG2', 1)]),
481        definition.entries)
482
483  def testParseThrowsOnUnknownDirective(self):
484    test_data = """
485      // GENERATED_JAVA_UNKNOWN: Value
486      enum EnumName {
487        VALUE_ONE,
488      };
489    """.split('\n')
490    with self.assertRaises(Exception):
491      HeaderParser(test_data).ParseDefinitions()
492
493  def testParseReturnsEmptyListWithoutDirectives(self):
494    test_data = """
495      enum EnumName {
496        VALUE_ONE,
497      };
498    """.split('\n')
499    self.assertEqual([], HeaderParser(test_data).ParseDefinitions())
500
501  def testParseEnumClass(self):
502    test_data = """
503      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
504      enum class Foo {
505        FOO_A,
506      };
507    """.split('\n')
508    definitions = HeaderParser(test_data).ParseDefinitions()
509    self.assertEqual(1, len(definitions))
510    definition = definitions[0]
511    self.assertEqual('Foo', definition.class_name)
512    self.assertEqual('test.namespace', definition.enum_package)
513    self.assertEqual(collections.OrderedDict([('A', 0)]),
514                     definition.entries)
515
516  def testParseEnumClassOneValueSubstringOfAnother(self):
517    test_data = """
518      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
519      enum class SafeBrowsingStatus {
520        kChecking = 0,
521        kEnabled = 1,
522        kDisabled = 2,
523        kDisabledByAdmin = 3,
524        kDisabledByExtension = 4,
525        kEnabledStandard = 5,
526        kEnabledEnhanced = 6,
527        // New enum values must go above here.
528        kMaxValue = kEnabledEnhanced,
529      };
530    """.split('\n')
531    definitions = HeaderParser(test_data).ParseDefinitions()
532    self.assertEqual(1, len(definitions))
533    definition = definitions[0]
534    self.assertEqual('SafeBrowsingStatus', definition.class_name)
535    self.assertEqual('test.namespace', definition.enum_package)
536    self.assertEqual(
537        collections.OrderedDict([
538            ('CHECKING', '0'),
539            ('ENABLED', '1'),
540            ('DISABLED', '2'),
541            ('DISABLED_BY_ADMIN', '3'),
542            ('DISABLED_BY_EXTENSION', '4'),
543            ('ENABLED_STANDARD', '5'),
544            ('ENABLED_ENHANCED', '6'),
545            ('MAX_VALUE', 'ENABLED_ENHANCED'),
546        ]), definition.entries)
547    self.assertEqual(
548        collections.OrderedDict([
549            ('MAX_VALUE', 'New enum values must go above here.')
550        ]), definition.comments)
551
552  def testParseEnumWithConditionallyDefinedValues(self):
553    test_data = """
554// GENERATED_JAVA_ENUM_PACKAGE: test.namespace
555// GENERATED_JAVA_PREFIX_TO_STRIP: TERMINATION_STATUS_
556enum TerminationStatus {
557  // Zero exit status.
558  TERMINATION_STATUS_NORMAL_TERMINATION = 0,
559  // Child hasn't exited yet.
560  TERMINATION_STATUS_STILL_RUNNING = 4,
561#if BUILDFLAG(IS_CHROMEOS)
562  // OOM-killer killed the process on ChromeOS.
563  TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM = 5,
564#endif
565#if BUILDFLAG(IS_ANDROID)
566  // On Android processes are spawned from the system Zygote and we do not get
567  // the termination status.
568  TERMINATION_STATUS_OOM_PROTECTED = 6,
569#endif
570  // Out of memory.
571  TERMINATION_STATUS_OOM = 8,
572#if BUILDFLAG(IS_WIN)
573  // On Windows, the OS terminated process due to code integrity failure.
574  TERMINATION_STATUS_INTEGRITY_FAILURE = 9,
575#endif
576#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
577  TERMINATION_STATUS_TEN = 10,
578#if BUILDFLAG(IS_POSIX)
579  TERMINATION_STATUS_ELEVEN = 11,
580#endif
581#endif
582#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
583  TERMINATION_STATUS_TWELVE = 12,
584#endif
585};
586    """.split('\n')
587    definitions = HeaderParser(test_data).ParseDefinitions()
588    self.assertEqual(1, len(definitions))
589    definition = definitions[0]
590    self.assertEqual('TerminationStatus', definition.class_name)
591    self.assertEqual('test.namespace', definition.enum_package)
592    self.assertEqual(
593        collections.OrderedDict([
594            ('NORMAL_TERMINATION', '0'),
595            ('STILL_RUNNING', '4'),
596            # PROCESS_WAS_KILLED_BY_OOM value should not appear here.
597            #
598            # OOM_PROTECTED should appear because the script supports the case
599            # where '#if BUILDFLAG(IS_ANDROID)' is used.
600            ('OOM_PROTECTED', '6'),
601            ('OOM', '8'),
602            # INTEGRITY_FAILURE value should not appear here.
603            # TEN and ELEVEN should not appear here.
604            ('TWELVE', '12'),
605        ]),
606        definition.entries)
607    self.assertEqual(
608        collections.OrderedDict([
609            ('NORMAL_TERMINATION', 'Zero exit status.'),
610            ('STILL_RUNNING', 'Child hasn\'t exited yet.'),
611            ('OOM_PROTECTED',
612             'On Android processes are spawned from the system Zygote and we ' +
613             'do not get the termination status.'),
614            ('OOM', 'Out of memory.'),
615        ]), definition.comments)
616
617  def testParseEnumStruct(self):
618    test_data = """
619      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
620      enum struct Foo {
621        FOO_A,
622      };
623    """.split('\n')
624    definitions = HeaderParser(test_data).ParseDefinitions()
625    self.assertEqual(1, len(definitions))
626    definition = definitions[0]
627    self.assertEqual('Foo', definition.class_name)
628    self.assertEqual('test.namespace', definition.enum_package)
629    self.assertEqual(collections.OrderedDict([('A', 0)]),
630                     definition.entries)
631
632  def testParseFixedTypeEnum(self):
633    test_data = """
634      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
635      enum Foo : int {
636        FOO_A,
637      };
638    """.split('\n')
639    definitions = HeaderParser(test_data).ParseDefinitions()
640    self.assertEqual(1, len(definitions))
641    definition = definitions[0]
642    self.assertEqual('Foo', definition.class_name)
643    self.assertEqual('test.namespace', definition.enum_package)
644    self.assertEqual('int', definition.fixed_type)
645    self.assertEqual(collections.OrderedDict([('A', 0)]),
646                     definition.entries)
647
648  def testParseFixedTypeEnumClass(self):
649    test_data = """
650      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
651      enum class Foo: unsigned short {
652        FOO_A,
653      };
654    """.split('\n')
655    definitions = HeaderParser(test_data).ParseDefinitions()
656    self.assertEqual(1, len(definitions))
657    definition = definitions[0]
658    self.assertEqual('Foo', definition.class_name)
659    self.assertEqual('test.namespace', definition.enum_package)
660    self.assertEqual('unsigned short', definition.fixed_type)
661    self.assertEqual(collections.OrderedDict([('A', 0)]),
662                     definition.entries)
663
664  def testParseUnknownFixedTypeRaises(self):
665    test_data = """
666      // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
667      enum class Foo: foo_type {
668        FOO_A,
669      };
670    """.split('\n')
671    with self.assertRaises(Exception):
672      HeaderParser(test_data).ParseDefinitions()
673
674  def testParseSimpleMultiLineDirective(self):
675    test_data = """
676      // GENERATED_JAVA_ENUM_PACKAGE: (
677      //   test.namespace)
678      // GENERATED_JAVA_CLASS_NAME_OVERRIDE: Bar
679      enum Foo {
680        FOO_A,
681      };
682    """.split('\n')
683    definitions = HeaderParser(test_data).ParseDefinitions()
684    self.assertEqual('test.namespace', definitions[0].enum_package)
685    self.assertEqual('Bar', definitions[0].class_name)
686
687  def testParseMultiLineDirective(self):
688    test_data = """
689      // GENERATED_JAVA_ENUM_PACKAGE: (te
690      //   st.name
691      //   space)
692      enum Foo {
693        FOO_A,
694      };
695    """.split('\n')
696    definitions = HeaderParser(test_data).ParseDefinitions()
697    self.assertEqual('test.namespace', definitions[0].enum_package)
698
699  def testParseMultiLineDirectiveWithOtherDirective(self):
700    test_data = """
701      // GENERATED_JAVA_ENUM_PACKAGE: (
702      //   test.namespace)
703      // GENERATED_JAVA_CLASS_NAME_OVERRIDE: (
704      //   Ba
705      //   r
706      //   )
707      enum Foo {
708        FOO_A,
709      };
710    """.split('\n')
711    definitions = HeaderParser(test_data).ParseDefinitions()
712    self.assertEqual('test.namespace', definitions[0].enum_package)
713    self.assertEqual('Bar', definitions[0].class_name)
714
715  def testParseMalformedMultiLineDirectiveWithOtherDirective(self):
716    test_data = """
717      // GENERATED_JAVA_ENUM_PACKAGE: (
718      //   test.name
719      //   space
720      // GENERATED_JAVA_CLASS_NAME_OVERRIDE: Bar
721      enum Foo {
722        FOO_A,
723      };
724    """.split('\n')
725    with self.assertRaises(Exception):
726      HeaderParser(test_data).ParseDefinitions()
727
728  def testParseMalformedMultiLineDirective(self):
729    test_data = """
730      // GENERATED_JAVA_ENUM_PACKAGE: (
731      //   test.name
732      //   space
733      enum Foo {
734        FOO_A,
735      };
736    """.split('\n')
737    with self.assertRaises(Exception):
738      HeaderParser(test_data).ParseDefinitions()
739
740  def testParseMalformedMultiLineDirectiveShort(self):
741    test_data = """
742      // GENERATED_JAVA_ENUM_PACKAGE: (
743      enum Foo {
744        FOO_A,
745      };
746    """.split('\n')
747    with self.assertRaises(Exception):
748      HeaderParser(test_data).ParseDefinitions()
749
750  def testParseMalformedMultiLineDirectiveMissingBrackets(self):
751    test_data = """
752      // GENERATED_JAVA_ENUM_PACKAGE:
753      // test.namespace
754      enum Foo {
755        FOO_A,
756      };
757    """.split('\n')
758    with self.assertRaises(Exception):
759      HeaderParser(test_data).ParseDefinitions()
760
761  def testEnumValueAssignmentNoneDefined(self):
762    definition = EnumDefinition(original_enum_name='c', enum_package='p')
763    definition.AppendEntry('A', None)
764    definition.AppendEntry('B', None)
765    definition.AppendEntry('C', None)
766    definition.Finalize()
767    self.assertEqual(collections.OrderedDict([('A', 0),
768                                              ('B', 1),
769                                              ('C', 2)]),
770                     definition.entries)
771
772  def testEnumValueAssignmentAllDefined(self):
773    definition = EnumDefinition(original_enum_name='c', enum_package='p')
774    definition.AppendEntry('A', '1')
775    definition.AppendEntry('B', '2')
776    definition.AppendEntry('C', '3')
777    definition.Finalize()
778    self.assertEqual(collections.OrderedDict([('A', '1'),
779                                              ('B', '2'),
780                                              ('C', '3')]),
781                     definition.entries)
782
783  def testEnumValueAssignmentReferences(self):
784    definition = EnumDefinition(original_enum_name='c', enum_package='p')
785    definition.AppendEntry('A', None)
786    definition.AppendEntry('B', 'A')
787    definition.AppendEntry('C', None)
788    definition.AppendEntry('D', 'C')
789    definition.Finalize()
790    self.assertEqual(collections.OrderedDict([('A', 0),
791                                              ('B', 0),
792                                              ('C', 1),
793                                              ('D', 1)]),
794                     definition.entries)
795
796  def testEnumValueAssignmentSet(self):
797    definition = EnumDefinition(original_enum_name='c', enum_package='p')
798    definition.AppendEntry('A', None)
799    definition.AppendEntry('B', '2')
800    definition.AppendEntry('C', None)
801    definition.Finalize()
802    self.assertEqual(collections.OrderedDict([('A', 0),
803                                              ('B', 2),
804                                              ('C', 3)]),
805                     definition.entries)
806
807  def testEnumValueAssignmentSetReferences(self):
808    definition = EnumDefinition(original_enum_name='c', enum_package='p')
809    definition.AppendEntry('A', None)
810    definition.AppendEntry('B', 'A')
811    definition.AppendEntry('C', 'B')
812    definition.AppendEntry('D', None)
813    definition.Finalize()
814    self.assertEqual(collections.OrderedDict([('A', 0),
815                                              ('B', 0),
816                                              ('C', 0),
817                                              ('D', 1)]),
818                     definition.entries)
819
820  def testEnumValueAssignmentRaises(self):
821    definition = EnumDefinition(original_enum_name='c', enum_package='p')
822    definition.AppendEntry('A', None)
823    definition.AppendEntry('B', 'foo')
824    definition.AppendEntry('C', None)
825    with self.assertRaises(Exception):
826      definition.Finalize()
827
828  def testExplicitPrefixStripping(self):
829    definition = EnumDefinition(original_enum_name='c', enum_package='p')
830    definition.AppendEntry('P_A', None)
831    definition.AppendEntry('B', None)
832    definition.AppendEntry('P_C', None)
833    definition.AppendEntry('P_LAST', 'P_C')
834    definition.prefix_to_strip = 'P_'
835    definition.Finalize()
836    self.assertEqual(collections.OrderedDict([('A', 0),
837                                              ('B', 1),
838                                              ('C', 2),
839                                              ('LAST', 2)]),
840                     definition.entries)
841
842  def testImplicitPrefixStripping(self):
843    definition = EnumDefinition(original_enum_name='ClassName',
844                                enum_package='p')
845    definition.AppendEntry('CLASS_NAME_A', None)
846    definition.AppendEntry('CLASS_NAME_B', None)
847    definition.AppendEntry('CLASS_NAME_C', None)
848    definition.AppendEntry('CLASS_NAME_LAST', 'CLASS_NAME_C')
849    definition.Finalize()
850    self.assertEqual(collections.OrderedDict([('A', 0),
851                                              ('B', 1),
852                                              ('C', 2),
853                                              ('LAST', 2)]),
854                     definition.entries)
855
856  def testImplicitPrefixStrippingRequiresAllConstantsToBePrefixed(self):
857    definition = EnumDefinition(original_enum_name='Name',
858                                enum_package='p')
859    definition.AppendEntry('A', None)
860    definition.AppendEntry('B', None)
861    definition.AppendEntry('NAME_LAST', None)
862    definition.Finalize()
863    self.assertEqual(['A', 'B', 'NAME_LAST'], list(definition.entries.keys()))
864
865  def testGenerateThrowsOnEmptyInput(self):
866    with self.assertRaises(Exception):
867      original_do_parse = java_cpp_enum.DoParseHeaderFile
868      try:
869        java_cpp_enum.DoParseHeaderFile = lambda _: []
870        for _ in java_cpp_enum.DoGenerate(['file']):
871          pass
872      finally:
873        java_cpp_enum.DoParseHeaderFile = original_do_parse
874
875
876if __name__ == '__main__':
877  unittest.main()
878