import WebIDL


def WebIDLTest(parser, harness):
    parser.parse(
        """
      dictionary Dict2 : Dict1 {
        long child = 5;
        Dict1 aaandAnother;
      };
      dictionary Dict1 {
        long parent;
        double otherParent;
      };
    """
    )
    results = parser.finish()

    dict1 = results[1]
    dict2 = results[0]

    harness.check(len(dict1.members), 2, "Dict1 has two members")
    harness.check(len(dict2.members), 2, "Dict2 has four members")

    harness.check(
        dict1.members[0].identifier.name, "otherParent", "'o' comes before 'p'"
    )
    harness.check(
        dict1.members[1].identifier.name, "parent", "'o' really comes before 'p'"
    )
    harness.check(
        dict2.members[0].identifier.name, "aaandAnother", "'a' comes before 'c'"
    )
    harness.check(
        dict2.members[1].identifier.name, "child", "'a' really comes before 'c'"
    )

    # Test partial dictionary.
    parser = parser.reset()
    parser.parse(
        """
      dictionary A {
        long c;
        long g;
      };
      partial dictionary A {
        long h;
        long d;
      };
    """
    )
    results = parser.finish()

    dict1 = results[0]
    harness.check(len(dict1.members), 4, "Dict1 has four members")
    harness.check(dict1.members[0].identifier.name, "c", "c should be first")
    harness.check(dict1.members[1].identifier.name, "d", "d should come after c")
    harness.check(dict1.members[2].identifier.name, "g", "g should come after d")
    harness.check(dict1.members[3].identifier.name, "h", "h should be last")

    # Now reset our parser
    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
          dictionary Dict {
            long prop = 5;
            long prop;
          };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Should not allow name duplication in a dictionary")

    # Test no name duplication across normal and partial dictionary.
    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
          dictionary A {
            long prop = 5;
          };
          partial dictionary A {
            long prop;
          };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw, "Should not allow name duplication across normal and partial dictionary"
    )

    # Now reset our parser again
    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
          dictionary Dict1 : Dict2 {
            long prop = 5;
          };
          dictionary Dict2 : Dict3 {
            long prop2;
          };
          dictionary Dict3 {
            double prop;
          };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw, "Should not allow name duplication in a dictionary and its ancestor"
    )

    # More reset
    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
          interface Iface {};
          dictionary Dict : Iface {
            long prop;
          };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Should not allow non-dictionary parents for dictionaries")

    # Even more reset
    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A : B {};
            dictionary B : A {};
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Should not allow cycles in dictionary inheritance chains")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
              [LegacyNullToEmptyString] DOMString foo;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw, "Should not allow [LegacyNullToEmptyString] on dictionary members"
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(A arg);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Trailing dictionary arg must be optional")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional A arg);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Trailing dictionary arg must have a default value")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo((A or DOMString) arg);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Trailing union arg containing a dictionary must be optional")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional (A or DOMString) arg);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw, "Trailing union arg containing a dictionary must have a default value"
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(A arg1, optional long arg2);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Dictionary arg followed by optional arg must be optional")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional A arg1, optional long arg2);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Dictionary arg followed by optional arg must have default value")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(A arg1, optional long arg2, long arg3);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        not threw,
        "Dictionary arg followed by non-optional arg doesn't have to be optional",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo((A or DOMString) arg1, optional long arg2);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Union arg containing dictionary followed by optional arg must be optional",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional (A or DOMString) arg1, optional long arg2);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Union arg containing dictionary followed by optional arg must "
        "have a default value",
    )

    parser = parser.reset()
    parser.parse(
        """
            dictionary A {
            };
            interface X {
              undefined doFoo(A arg1, long arg2);
            };
        """
    )
    parser.finish()
    harness.ok(True, "Dictionary arg followed by required arg can be required")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional A? arg1 = {});
            };
        """
        )
        parser.finish()
    except Exception as x:
        threw = x

    harness.ok(threw, "Optional dictionary arg must not be nullable")
    harness.ok(
        "nullable" in str(threw),
        "Must have the expected exception for optional nullable dictionary arg",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
              required long x;
            };
            interface X {
              undefined doFoo(A? arg1);
            };
        """
        )
        parser.finish()
    except Exception as x:
        threw = x

    harness.ok(threw, "Required dictionary arg must not be nullable")
    harness.ok(
        "nullable" in str(threw),
        "Must have the expected exception for required nullable dictionary arg",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional (A or long)? arg1 = {});
            };
        """
        )
        parser.finish()
    except Exception as x:
        threw = x

    harness.ok(threw, "Dictionary arg must not be in an optional nullable union")
    harness.ok(
        "nullable" in str(threw),
        "Must have the expected exception for optional nullable union "
        "arg containing dictionary",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
              required long x;
            };
            interface X {
              undefined doFoo((A or long)? arg1);
            };
        """
        )
        parser.finish()
    except Exception as x:
        threw = x

    harness.ok(threw, "Dictionary arg must not be in a required nullable union")
    harness.ok(
        "nullable" in str(threw),
        "Must have the expected exception for required nullable union "
        "arg containing dictionary",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(sequence<A?> arg1);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(not threw, "Nullable union should be allowed in a sequence argument")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional (A or long?) arg1);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True
    harness.ok(threw, "Dictionary must not be in a union with a nullable type")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary A {
            };
            interface X {
              undefined doFoo(optional (long? or A) arg1);
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True
    harness.ok(threw, "A nullable type must not be in a union with a dictionary")

    parser = parser.reset()
    parser.parse(
        """
        dictionary A {
        };
        interface X {
          A? doFoo();
        };
    """
    )
    parser.finish()
    harness.ok(True, "Dictionary return value can be nullable")

    parser = parser.reset()
    parser.parse(
        """
        dictionary A {
        };
        interface X {
          undefined doFoo(optional A arg = {});
        };
    """
    )
    parser.finish()
    harness.ok(True, "Dictionary arg should actually parse")

    parser = parser.reset()
    parser.parse(
        """
        dictionary A {
        };
        interface X {
          undefined doFoo(optional (A or DOMString) arg = {});
        };
    """
    )
    parser.finish()
    harness.ok(True, "Union arg containing a dictionary should actually parse")

    parser = parser.reset()
    parser.parse(
        """
        dictionary A {
        };
        interface X {
          undefined doFoo(optional (A or DOMString) arg = "abc");
        };
    """
    )
    parser.finish()
    harness.ok(
        True,
        "Union arg containing a dictionary with string default should actually parse",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              Foo foo;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Member type must not be its Dictionary.")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo3 : Foo {
              short d;
            };

            dictionary Foo2 : Foo3 {
              boolean c;
            };

            dictionary Foo1 : Foo2 {
              long a;
            };

            dictionary Foo {
              Foo1 b;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Member type must not be a Dictionary that inherits from its Dictionary.",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              (Foo or DOMString)[]? b;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Member type must not be a Nullable type "
        "whose inner type includes its Dictionary.",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              (DOMString or Foo) b;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Member type must not be a Union type, one of "
        "whose member types includes its Dictionary.",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              sequence<sequence<sequence<Foo>>> c;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Member type must not be a Sequence type "
        "whose element type includes its Dictionary.",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              (DOMString or Foo)[] d;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Member type must not be an Array type "
        "whose element type includes its Dictionary.",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              Foo1 b;
            };

            dictionary Foo3 {
              Foo d;
            };

            dictionary Foo2 : Foo3 {
              short c;
            };

            dictionary Foo1 : Foo2 {
              long a;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(
        threw,
        "Member type must not be a Dictionary, one of whose "
        "members or inherited members has a type that includes "
        "its Dictionary.",
    )

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
            };

            dictionary Bar {
              Foo? d;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Member type must not be a nullable dictionary")

    parser = parser.reset()
    parser.parse(
        """
        dictionary Foo {
          unrestricted float  urFloat = 0;
          unrestricted float  urFloat2 = 1.1;
          unrestricted float  urFloat3 = -1.1;
          unrestricted float? urFloat4 = null;
          unrestricted float  infUrFloat = Infinity;
          unrestricted float  negativeInfUrFloat = -Infinity;
          unrestricted float  nanUrFloat = NaN;

          unrestricted double  urDouble = 0;
          unrestricted double  urDouble2 = 1.1;
          unrestricted double  urDouble3 = -1.1;
          unrestricted double? urDouble4 = null;
          unrestricted double  infUrDouble = Infinity;
          unrestricted double  negativeInfUrDouble = -Infinity;
          unrestricted double  nanUrDouble = NaN;
        };
    """
    )
    parser.finish()
    harness.ok(True, "Parsing default values for unrestricted types succeeded.")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              double f = Infinity;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Only unrestricted values can be initialized to Infinity")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              double f = -Infinity;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              double f = NaN;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Only unrestricted values can be initialized to NaN")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              float f = Infinity;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Only unrestricted values can be initialized to Infinity")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              float f = -Infinity;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              float f = NaN;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(threw, "Only unrestricted values can be initialized to NaN")

    parser = parser.reset()
    threw = False
    try:
        parser.parse(
            """
            dictionary Foo {
              long module;
            };
        """
        )
        parser.finish()
    except WebIDL.WebIDLError:
        threw = True

    harness.ok(not threw, "Should be able to use 'module' as a dictionary member name")
