// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

#include "testenum.h"
#include "testutil.h"
#include <abstractmetaargument.h>
#include <abstractmetaenum.h>
#include <abstractmetafunction.h>
#include <abstractmetalang.h>
#include <abstractmetabuilder_p.h>
#include <enumtypeentry.h>
#include <flagstypeentry.h>
#include <parser/enumvalue.h>

#include <qtcompat.h>

#include <QtTest/QTest>

using namespace Qt::StringLiterals;

void TestEnum::testEnumCppSignature()
{
    const char cppCode[] = "\
    enum GlobalEnum { A, B };\n\
    \n\
    struct A {\n\
        enum ClassEnum { CA, CB };\n\
        void method(ClassEnum);\n\
    };\n\
    void func(A::ClassEnum);\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <enum-type name='GlobalEnum'/>\n\
        <value-type name='A'>\n\
            <enum-type name='ClassEnum'/>\n\
        </value-type>\n\
        <function signature='func(A::ClassEnum)'/>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
    QVERIFY(builder);
    AbstractMetaClassList classes = builder->classes();
    QCOMPARE(classes.size(), 1);

    AbstractMetaEnumList globalEnums = builder->globalEnums();
    QCOMPARE(globalEnums.size(), 1);
    QCOMPARE(globalEnums.constFirst().name(), u"GlobalEnum");

    // enum as parameter of a function
    const auto functions = builder->globalFunctions();
    QCOMPARE(functions.size(), 1);
    QCOMPARE(functions.constFirst()->arguments().size(), 1);
    QCOMPARE(functions.constFirst()->arguments().constFirst().type().cppSignature(),
             u"A::ClassEnum");

    // enum as parameter of a method
    const auto classA = AbstractMetaClass::findClass(classes, u"A");
    QCOMPARE(classA->enums().size(), 1);
    const auto funcs = classA->queryFunctionsByName(u"method"_s);
    QVERIFY(!funcs.isEmpty());
    const auto method = funcs.constFirst();
    AbstractMetaArgument arg = method->arguments().constFirst();
    QCOMPARE(arg.type().name(), u"ClassEnum");
    QCOMPARE(arg.type().cppSignature(), u"A::ClassEnum");
    QCOMPARE(functions.constFirst()->arguments().size(), 1);
    arg = functions.constFirst()->arguments().constFirst();
    QCOMPARE(arg.type().name(), u"ClassEnum");
    QCOMPARE(arg.type().cppSignature(), u"A::ClassEnum");

    AbstractMetaEnumList classEnums = classA->enums();
    QVERIFY(!classEnums.isEmpty());
    QCOMPARE(classEnums.constFirst().name(), u"ClassEnum");
    auto e = AbstractMetaClass::findEnumValue(classes, u"CA"_s);
    QVERIFY(e.has_value());
    e = AbstractMetaClass::findEnumValue(classes, u"ClassEnum::CA"_s);
    QVERIFY(e.has_value());
}

void TestEnum::testEnumWithApiVersion()
{
    const char cppCode[] = "\
    struct A {\n\
        enum ClassEnum { EnumA, EnumB };\n\
        enum ClassEnum2 { EnumC, EnumD };\n\
    };\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <value-type name='A'>\n\
            <enum-type name='ClassEnum' since='0.1'/>\n\
            <enum-type name='ClassEnum2' since='0.2'/>\n\
        </value-type>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
                                                                true, u"0.1"_s));
    QVERIFY(builder);
    AbstractMetaClassList classes = builder->classes();
    QCOMPARE(classes.size(), 1);
    QCOMPARE(classes[0]->enums().size(), 1);
}

void TestEnum::testAnonymousEnum()
{
    const char cppCode[] = "\
    enum { Global0, Global1 };\n\
    struct A {\n\
        enum { A0, A1 };\n\
        enum { isThis = true, isThat = false };\n\
    };\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <!-- Uses the first value of the enum to identify it. -->\n\
        <enum-type identified-by-value='Global0'/>\n\
        <value-type name='A'>\n\
            <!-- Uses the second value of the enum to identify it. -->\n\
            <enum-type identified-by-value='A1'/>\n\
            <enum-type identified-by-value='isThis'/>\n\
        </value-type>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
    QVERIFY(builder);

    AbstractMetaEnumList globalEnums = builder->globalEnums();
    QCOMPARE(globalEnums.size(), 1);
    QCOMPARE(globalEnums.constFirst().typeEntry()->qualifiedCppName(),
             u"Global0");
    QVERIFY(globalEnums.constFirst().isAnonymous());

    AbstractMetaClassList classes = builder->classes();
    QCOMPARE(classes.size(), 1);
    QCOMPARE(classes[0]->enums().size(), 2);

    auto anonEnumA1 = classes[0]->findEnum(u"A1"_s);
    QVERIFY(anonEnumA1.has_value());
    QVERIFY(anonEnumA1->isAnonymous());
    QCOMPARE(anonEnumA1->typeEntry()->qualifiedCppName(), u"A::A1");

    AbstractMetaEnumValue enumValueA0 = anonEnumA1->values().constFirst();
    QCOMPARE(enumValueA0.name(), u"A0");
    QCOMPARE(enumValueA0.value().value(), 0);
    QCOMPARE(enumValueA0.stringValue(), QString());

    AbstractMetaEnumValue enumValueA1 = anonEnumA1->values().constLast();
    QCOMPARE(enumValueA1.name(), u"A1");
    QCOMPARE(enumValueA1.value().value(), 1);
    QCOMPARE(enumValueA1.stringValue(), QString());

    auto anonEnumIsThis = classes[0]->findEnum(u"isThis"_s);
    QVERIFY(anonEnumIsThis.has_value());
    QVERIFY(anonEnumIsThis->isAnonymous());
    QCOMPARE(anonEnumIsThis->typeEntry()->qualifiedCppName(), u"A::isThis");

    AbstractMetaEnumValue enumValueIsThis = anonEnumIsThis->values().constFirst();
    QCOMPARE(enumValueIsThis.name(), u"isThis");
    QCOMPARE(enumValueIsThis.value().value(), static_cast<int>(true));
    QCOMPARE(enumValueIsThis.stringValue(), u"true");

    AbstractMetaEnumValue enumValueIsThat = anonEnumIsThis->values().constLast();
    QCOMPARE(enumValueIsThat.name(), u"isThat");
    QCOMPARE(enumValueIsThat.value().value(), static_cast<int>(false));
    QCOMPARE(enumValueIsThat.stringValue(), u"false");
}

void TestEnum::testGlobalEnums()
{
    const char cppCode[] = "\
    enum EnumA { A0, A1 };\n\
    enum EnumB { B0 = 2, B1 = 0x4 };\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <enum-type name='EnumA'/>\n\
        <enum-type name='EnumB'/>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
    QVERIFY(builder);

    AbstractMetaEnumList globalEnums = builder->globalEnums();
    QCOMPARE(globalEnums.size(), 2);

    AbstractMetaEnum enumA = globalEnums.constFirst();
    QCOMPARE(enumA.typeEntry()->qualifiedCppName(), u"EnumA");

    AbstractMetaEnumValue enumValueA0 = enumA.values().constFirst();
    QCOMPARE(enumValueA0.name(), u"A0");
    QCOMPARE(enumValueA0.value().value(), 0);
    QCOMPARE(enumValueA0.stringValue(), QString());

    AbstractMetaEnumValue enumValueA1 = enumA.values().constLast();
    QCOMPARE(enumValueA1.name(), u"A1");
    QCOMPARE(enumValueA1.value().value(), 1);
    QCOMPARE(enumValueA1.stringValue(), QString());

    AbstractMetaEnum enumB = globalEnums.constLast();
    QCOMPARE(enumB.typeEntry()->qualifiedCppName(), u"EnumB");

    AbstractMetaEnumValue enumValueB0 = enumB.values().constFirst();
    QCOMPARE(enumValueB0.name(), u"B0");
    QCOMPARE(enumValueB0.value().value(), 2);
    QCOMPARE(enumValueB0.stringValue(), u"2");

    AbstractMetaEnumValue enumValueB1 = enumB.values().constLast();
    QCOMPARE(enumValueB1.name(), u"B1");
    QCOMPARE(enumValueB1.value().value(), 4);
    QCOMPARE(enumValueB1.stringValue(), u"0x4");
}

void TestEnum::testEnumValueFromNeighbourEnum()
{
    const char cppCode[] = "\
    namespace A {\n\
        enum EnumA { ValueA0, ValueA1 };\n\
        enum EnumB { ValueB0 = A::ValueA1, ValueB1 = ValueA0 };\n\
    };\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <namespace-type name='A'>\n\
            <enum-type name='EnumA'/>\n\
            <enum-type name='EnumB'/>\n\
        </namespace-type>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
    QVERIFY(builder);

    AbstractMetaClassList classes = builder->classes();
    QCOMPARE(classes.size(), 1);
    QCOMPARE(classes[0]->enums().size(), 2);

    auto enumA = classes[0]->findEnum(u"EnumA"_s);
    QVERIFY(enumA.has_value());
    QCOMPARE(enumA->typeEntry()->qualifiedCppName(), u"A::EnumA");

    AbstractMetaEnumValue enumValueA0 = enumA->values().constFirst();
    QCOMPARE(enumValueA0.name(), u"ValueA0");
    QCOMPARE(enumValueA0.value().value(), 0);
    QCOMPARE(enumValueA0.stringValue(), QString());

    AbstractMetaEnumValue enumValueA1 = enumA->values().constLast();
    QCOMPARE(enumValueA1.name(), u"ValueA1");
    QCOMPARE(enumValueA1.value().value(), 1);
    QCOMPARE(enumValueA1.stringValue(), QString());

    auto enumB = classes[0]->findEnum(u"EnumB"_s);
    QVERIFY(enumB.has_value());
    QCOMPARE(enumB->typeEntry()->qualifiedCppName(), u"A::EnumB");

    AbstractMetaEnumValue enumValueB0 = enumB->values().constFirst();
    QCOMPARE(enumValueB0.name(), u"ValueB0");
    QCOMPARE(enumValueB0.value().value(), 1);
    QCOMPARE(enumValueB0.stringValue(), u"A::ValueA1");

    AbstractMetaEnumValue enumValueB1 = enumB->values().constLast();
    QCOMPARE(enumValueB1.name(), u"ValueB1");
    QCOMPARE(enumValueB1.value().value(), 0);
    QCOMPARE(enumValueB1.stringValue(), u"ValueA0");
}

void TestEnum::testEnumValueFromExpression()
{
    const char cppCode[] = "\
    struct A {\n\
        enum EnumA : unsigned {\n\
            ValueA0 = 3u,\n\
            ValueA1 = ~3u,\n\
            ValueA2 = 0xffffffff,\n\
            ValueA3 = 0xf0,\n\
            ValueA4 = 8 |ValueA3,\n\
            ValueA5 = ValueA3|32,\n\
            ValueA6 = ValueA3 >> 1,\n\
            ValueA7 = ValueA3 << 1\n\
        };\n\
        enum EnumB : int {\n\
            ValueB0 = ~3,\n\
        };\n\
    };\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <value-type name='A'>\n\
            <enum-type name='EnumA'/>\n\
            <enum-type name='EnumB'/>\n\
        </value-type>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
    QVERIFY(builder);

    AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), u"A");
    QVERIFY(classA);

    auto enumA = classA->findEnum(u"EnumA"_s);
    QVERIFY(enumA.has_value());
    QVERIFY(!enumA->isSigned());
    QCOMPARE(enumA->typeEntry()->qualifiedCppName(), u"A::EnumA");

    AbstractMetaEnumValue valueA0 = enumA->values().at(0);
    QCOMPARE(valueA0.name(), u"ValueA0");
    QCOMPARE(valueA0.stringValue(), u"3u");
    QCOMPARE(valueA0.value().unsignedValue(), 3u);

    AbstractMetaEnumValue valueA1 = enumA->values().at(1);
    QCOMPARE(valueA1.name(), u"ValueA1");
    QCOMPARE(valueA1.stringValue(), u"~3u");
    QCOMPARE(valueA1.value().unsignedValue(), ~3u);

    AbstractMetaEnumValue valueA2 = enumA->values().at(2);
    QCOMPARE(valueA2.name(), u"ValueA2");
    QCOMPARE(valueA2.stringValue(), u"0xffffffff");
    QCOMPARE(valueA2.value().unsignedValue(), 0xffffffffu);

    AbstractMetaEnumValue valueA3 = enumA->values().at(3);
    QCOMPARE(valueA3.name(), u"ValueA3");
    QCOMPARE(valueA3.stringValue(), u"0xf0");
    QCOMPARE(valueA3.value().unsignedValue(), 0xf0u);

    AbstractMetaEnumValue valueA4 = enumA->values().at(4);
    QCOMPARE(valueA4.name(), u"ValueA4");
    QCOMPARE(valueA4.stringValue(), u"8 |ValueA3");
    QCOMPARE(valueA4.value().unsignedValue(), 8|0xf0u);

    AbstractMetaEnumValue valueA5 = enumA->values().at(5);
    QCOMPARE(valueA5.name(), u"ValueA5");
    QCOMPARE(valueA5.stringValue(), u"ValueA3|32");
    QCOMPARE(valueA5.value().unsignedValue(), 0xf0u|32);

    AbstractMetaEnumValue valueA6 = enumA->values().at(6);
    QCOMPARE(valueA6.name(), u"ValueA6");
    QCOMPARE(valueA6.stringValue(), u"ValueA3 >> 1");
    QCOMPARE(valueA6.value().unsignedValue(), 0xf0u >> 1);

    AbstractMetaEnumValue valueA7 = enumA->values().at(7);
    QCOMPARE(valueA7.name(), u"ValueA7");
    QCOMPARE(valueA7.stringValue(), u"ValueA3 << 1");
    QCOMPARE(valueA7.value().unsignedValue(), 0xf0u << 1);

   const auto enumB = classA->findEnum(u"EnumB"_s);
   QVERIFY(enumB.has_value());
   QVERIFY(enumB->isSigned());
   QCOMPARE(enumB->typeEntry()->qualifiedCppName(), u"A::EnumB");
   QCOMPARE(enumB->values().size(), 1);
   const AbstractMetaEnumValue valueB0 = enumB->values().at(0);
   QCOMPARE(valueB0.name(), u"ValueB0");
   QCOMPARE(valueB0.stringValue(), u"~3");
   QCOMPARE(valueB0.value().value(), ~3);
}

void TestEnum::testPrivateEnum()
{
    const char cppCode[] = "\
    class A {\n\
    private:\n\
        enum PrivateEnum { Priv0 = 0x0f, Priv1 = 0xf0 };\n\
    public:\n\
        enum PublicEnum { Pub0 = Priv0, Pub1 = A::Priv1 };\n\
    };\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <value-type name='A'>\n\
            <enum-type name='PublicEnum'/>\n\
        </value-type>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
    QVERIFY(builder);

    const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
    QVERIFY(classA);
    QCOMPARE(classA->enums().size(), 2);

    auto privateEnum = classA->findEnum(u"PrivateEnum"_s);
    QVERIFY(privateEnum.has_value());
    QVERIFY(privateEnum->isPrivate());
    QCOMPARE(privateEnum->typeEntry()->qualifiedCppName(), u"A::PrivateEnum");

    auto publicEnum = classA->findEnum(u"PublicEnum"_s);
    QVERIFY(publicEnum.has_value());
    QCOMPARE(publicEnum->typeEntry()->qualifiedCppName(), u"A::PublicEnum");

    AbstractMetaEnumValue pub0 = publicEnum->values().constFirst();
    QCOMPARE(pub0.name(), u"Pub0");
    QCOMPARE(pub0.value().value(), 0x0f);
    QCOMPARE(pub0.stringValue(), u"Priv0");

    AbstractMetaEnumValue pub1 = publicEnum->values().constLast();
    QCOMPARE(pub1.name(), u"Pub1");
    QCOMPARE(pub1.value().value(), 0xf0);
    QCOMPARE(pub1.stringValue(), u"A::Priv1");
}

void TestEnum::testTypedefEnum()
{
    const char cppCode[] = "\
    typedef enum EnumA {\n\
        A0,\n\
        A1,\n\
    } EnumA;\n";
    const char xmlCode[] = "\
    <typesystem package=\"Foo\">\n\
        <enum-type name='EnumA'/>\n\
    </typesystem>\n";

    QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
    QVERIFY(builder);

    AbstractMetaEnumList globalEnums = builder->globalEnums();
    QCOMPARE(globalEnums.size(), 1);

    AbstractMetaEnum enumA = globalEnums.constFirst();
    QCOMPARE(enumA.typeEntry()->qualifiedCppName(), u"EnumA");

    AbstractMetaEnumValue enumValueA0 = enumA.values().constFirst();
    QCOMPARE(enumValueA0.name(), u"A0");
    QCOMPARE(enumValueA0.value().value(), 0);
    QCOMPARE(enumValueA0.stringValue(), u"");

    AbstractMetaEnumValue enumValueA1 = enumA.values().constLast();
    QCOMPARE(enumValueA1.name(), u"A1");
    QCOMPARE(enumValueA1.value().value(), 1);
    QCOMPARE(enumValueA1.stringValue(), QString());
}

// Helper classes and functions for testing enum default value fixing.
// Put the AbstractMetaBuilder into test fixture struct to avoid having
// to re-parse for each data row.

struct EnumDefaultValuesFixture
{
    std::shared_ptr<AbstractMetaBuilder> builder;

    AbstractMetaType globalEnum;
    AbstractMetaType testEnum;
    AbstractMetaType testOptions;
};

Q_DECLARE_METATYPE(EnumDefaultValuesFixture)
Q_DECLARE_METATYPE(AbstractMetaType)

static int populateDefaultValuesFixture(EnumDefaultValuesFixture *fixture)
{
    static const char cppCode[] =R"(
enum GlobalEnum { GE1, GE2 };
namespace Test1
{
namespace Test2
{
   enum Enum1 { E1, E2 };
   enum Option { O1, O2 };
} // namespace Test2
} // namespace Test1
)";
    static const char xmlCode[] = R"(
<typesystem package="Foo">
    <enum-type name='GlobalEnum'/>
    <namespace-type name='Test1'>
        <namespace-type name='Test2'>
            <enum-type name='Enum1'/>
            <enum-type name='Option' flags='Options'/>
        </namespace-type>
    </namespace-type>
</typesystem>
)";

    fixture->builder.reset(TestUtil::parse(cppCode, xmlCode, false));
    if (!fixture->builder)
        return -1;

    const auto globalEnums = fixture->builder->globalEnums();
    if (globalEnums.size() != 1)
        return -2;

    fixture->globalEnum = AbstractMetaType(globalEnums.constFirst().typeEntry());
    fixture->globalEnum.decideUsagePattern();

    AbstractMetaClassCPtr testNamespace;
    for (const auto &c : fixture->builder->classes()) {
        if (c->name() == u"Test2") {
            testNamespace = c;
            break;
        }
    }
    if (!testNamespace)
        return -3;

    const auto namespaceEnums = testNamespace->enums();
    if (namespaceEnums.size() != 2)
        return -4;
    QList<EnumTypeEntryCPtr > enumTypeEntries{
        std::static_pointer_cast<const EnumTypeEntry>(namespaceEnums.at(0).typeEntry()),
        std::static_pointer_cast<const EnumTypeEntry>(namespaceEnums.at(1).typeEntry())};
    if (enumTypeEntries.constFirst()->flags())
        std::swap(enumTypeEntries[0], enumTypeEntries[1]);
    fixture->testEnum = AbstractMetaType(enumTypeEntries.at(0));
    fixture->testEnum.decideUsagePattern();
    fixture->testOptions = AbstractMetaType(enumTypeEntries.at(1)->flags());
    fixture->testOptions.decideUsagePattern();
    return 0;
}

void TestEnum::testEnumDefaultValues_data()
{
    EnumDefaultValuesFixture fixture;
    const int setupOk = populateDefaultValuesFixture(&fixture);

    QTest::addColumn<EnumDefaultValuesFixture>("fixture");
    QTest::addColumn<int>("setupOk"); // To verify setup
    QTest::addColumn<AbstractMetaType>("metaType"); // Type and parameters for fixup
    QTest::addColumn<QString>("input");
    QTest::addColumn<QString>("expected");

    // Global should just remain unmodified
    QTest::newRow("global") << fixture << setupOk
        << fixture.globalEnum << "GE1" << "GE1";
    QTest::newRow("global-int") << fixture << setupOk
        << fixture.globalEnum << "42" << "42";
    QTest::newRow("global-hex-int") << fixture << setupOk
        << fixture.globalEnum << "0x10" << "0x10";
    QTest::newRow("global-int-cast") << fixture << setupOk
        << fixture.globalEnum << "GlobalEnum(-1)" << "GlobalEnum(-1)";

    // Namespaced enum as number should remain unmodified
    QTest::newRow("namespace-enum-int") << fixture << setupOk
        << fixture.testEnum << "42" << "42";
    QTest::newRow("namespace-enum-hex-int") << fixture << setupOk
        << fixture.testEnum << "0x10" << "0x10";
    // Partial qualification of namespaced enum
    QTest::newRow("namespace-enum-qualified") << fixture << setupOk
        << fixture.testEnum << "Enum1::E1" << "Test1::Test2::Enum1::E1";
    // Unqualified namespaced enums
    QTest::newRow("namespace-enum-unqualified") << fixture << setupOk
        << fixture.testEnum << "E1" << "Test1::Test2::Enum1::E1";
    // Namespaced enums cast from int should be qualified by scope
    QTest::newRow("namespace-enum-int-cast") << fixture << setupOk
        << fixture.testEnum << "Enum1(-1)" << "Test1::Test2::Enum1(-1)";

    // Namespaced option as number should remain unmodified
    QTest::newRow("namespace-option-int") << fixture << setupOk
        << fixture.testOptions << "0x10" << "0x10";
    QTest::newRow("namespace-option-expression") << fixture << setupOk
        << fixture.testOptions << "0x10 | 0x20" << "0x10 | 0x20";
    QTest::newRow("namespace-option-expression1") << fixture << setupOk
        << fixture.testOptions << "0x10 | Test1::Test2::Option::O1"
        << "0x10 | Test1::Test2::Option::O1";
    QTest::newRow("namespace-option-expression2") << fixture << setupOk
        << fixture.testOptions << "0x10 | O1" << "0x10 | Test1::Test2::Option::O1";
    // Complicated expressions - should remain unmodified
    QTest::newRow("namespace-option-expression-paren") << fixture << setupOk
        << fixture.testOptions << "0x10 | (0x20 | 0x40 | O1)"
        << "0x10 | (0x20 | 0x40 | O1)";

    // Option: Cast Enum from int should be qualified
    QTest::newRow("namespace-option-int-cast") << fixture << setupOk
        << fixture.testOptions << "Option(0x10)" << "Test1::Test2::Option(0x10)";
    // Option: Cast Flags from int should be qualified
    QTest::newRow("namespace-options-int-cast") << fixture << setupOk
        << fixture.testOptions << "Options(0x10 | 0x20)" << "Test1::Test2::Options(0x10 | 0x20)";
    QTest::newRow("namespace-option-cast-expression1") << fixture << setupOk
        << fixture.testOptions << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)"
        << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)";
    QTest::newRow("namespace-option-cast-expression2") << fixture << setupOk
        << fixture.testOptions << "Options(0x10 | O1)"
        << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)";
}

void TestEnum::testEnumDefaultValues()
{
    QFETCH(EnumDefaultValuesFixture, fixture);
    QFETCH(int, setupOk);
    QFETCH(AbstractMetaType, metaType);
    QFETCH(QString, input);
    QFETCH(QString, expected);
    QCOMPARE(setupOk, 0);
    const QString actual = fixture.builder->fixEnumDefault(metaType,  input);
    QCOMPARE(actual, expected);
}

QTEST_APPLESS_MAIN(TestEnum)
