File indexing completed on 2024-06-16 04:23:10

0001 /*
0002     SPDX-FileCopyrightText: 2012 Miha Čančula <miha@noughmad.eu>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "test_templateclassgenerator.h"
0008 #include "codegen_tests_config.h"
0009 
0010 #include "language/codegen/templateclassgenerator.h"
0011 #include "language/codegen/documentchangeset.h"
0012 #include "language/codegen/sourcefiletemplate.h"
0013 #include "language/codegen/templaterenderer.h"
0014 #include "language/codegen/templatesmodel.h"
0015 
0016 #include "tests/autotestshell.h"
0017 #include "tests/testcore.h"
0018 #include <tests/testhelpers.h>
0019 
0020 /*
0021 * CHECK_TEMPLATE_VARIABLE expects second parameter as type with 'to' instead of 'Q' in begin
0022 * For example, QString -> toString
0023 */
0024 #define CHECK_TEMPLATE_VARIABLE(name, type, val)            \
0025 QVERIFY(variables.contains(QStringLiteral(#name)));         \
0026 QCOMPARE(variables.value(QStringLiteral(#name)).type(), val)
0027 
0028 #define COMPARE_FILES(one, two)                             \
0029 QCOMPARE(QString(one.readAll()), QString(two.readAll()))    \
0030 
0031 using namespace KDevelop;
0032 
0033 namespace
0034 {
0035 void setLowercaseFileNames(std::unique_ptr<TemplateClassGenerator>& generator)
0036 {
0037     QHash<QString, QUrl> urls = generator->fileUrls();
0038     QHash<QString, QUrl>::const_iterator it = urls.constBegin();
0039     for (; it != urls.constEnd(); ++it) {
0040         QString fileName = it.value().fileName().toLower();
0041         QUrl base = it.value().resolved(QUrl(QStringLiteral("./%1").arg(fileName)));
0042         generator->setFileUrl(it.key(), base);
0043     }
0044 }
0045 }
0046 
0047 void TestTemplateClassGenerator::initTestCase()
0048 {
0049     AutoTestShell::init();
0050     TestCore::initialize(Core::NoUi);
0051 
0052     // Use a temporary directory for the template work
0053     tempDir.setAutoRemove(true);
0054     baseUrl = QUrl::fromLocalFile(tempDir.path() + '/');
0055 
0056     // Needed for extracting description out of template archives
0057     TemplatesModel model(QStringLiteral("kdevcodegentest"));
0058     model.refresh();
0059 
0060     description.members << VariableDescription(QStringLiteral("QString"), QStringLiteral("name"))
0061                         << VariableDescription(QStringLiteral("int"), QStringLiteral("number"))
0062                         << VariableDescription(QStringLiteral("SomeCustomType"), QStringLiteral("data"));
0063 
0064     FunctionDescription function;
0065     function.name = QStringLiteral("doSomething");
0066     function.isVirtual = true;
0067     function.arguments << VariableDescription(QStringLiteral("double"), QStringLiteral("howMuch"))
0068                        << VariableDescription(QStringLiteral("bool"), QStringLiteral("doSomethingElse"));
0069 
0070     VariableDescriptionList args;
0071     VariableDescriptionList returnArgs;
0072     returnArgs << VariableDescription(QStringLiteral("int"), QStringLiteral("someOtherNumber"));
0073     FunctionDescription otherFunction(QStringLiteral("getSomeOtherNumber"), args, returnArgs);
0074     description.methods << function << otherFunction;
0075 }
0076 
0077 void TestTemplateClassGenerator::cleanupTestCase()
0078 {
0079     TestCore::shutdown();
0080 }
0081 
0082 void TestTemplateClassGenerator::fileLabelsCpp()
0083 {
0084     auto generator = loadTemplate(QStringLiteral("test_cpp"));
0085     RETURN_IF_TEST_FAILED();
0086 
0087     QHash<QString,QString> labels = generator->fileLabels();
0088     QCOMPARE(labels.size(), 2);
0089 
0090     /*
0091      * File labels can be translated, so we don't check their equality here.
0092      * But they have to be present and non-empty
0093      */
0094     QVERIFY(labels.contains(QStringLiteral("Header")));
0095     QVERIFY(!labels[QStringLiteral("Header")].isEmpty());
0096     QVERIFY(labels.contains(QStringLiteral("Implementation")));
0097     QVERIFY(!labels[QStringLiteral("Implementation")].isEmpty());
0098 }
0099 
0100 void TestTemplateClassGenerator::fileLabelsYaml()
0101 {
0102     auto generator = loadTemplate(QStringLiteral("test_yaml"));
0103     RETURN_IF_TEST_FAILED();
0104 
0105     QHash<QString,QString> labels = generator->fileLabels();
0106     QCOMPARE(labels.size(), 1);
0107 
0108     QVERIFY(labels.contains(QStringLiteral("Description")));
0109     QVERIFY(!labels[QStringLiteral("Description")].isEmpty());
0110 }
0111 
0112 void TestTemplateClassGenerator::defaultFileUrlsCpp()
0113 {
0114     auto generator = loadTemplate(QStringLiteral("test_cpp"));
0115     RETURN_IF_TEST_FAILED();
0116 
0117     QHash<QString,QUrl> files = generator->fileUrls();
0118     QCOMPARE(files.size(), 2);
0119 
0120     QVERIFY(files.contains(QStringLiteral("Header")));
0121     QCOMPARE(files[QStringLiteral("Header")], baseUrl.resolved(QUrl(QStringLiteral("ClassName.h"))));
0122 
0123     QVERIFY(files.contains(QStringLiteral("Implementation")));
0124     QCOMPARE(files[QStringLiteral("Implementation")], baseUrl.resolved(QUrl(QStringLiteral("ClassName.cpp"))));
0125 }
0126 
0127 void TestTemplateClassGenerator::defaultFileUrlsYaml()
0128 {
0129     auto generator = loadTemplate(QStringLiteral("test_yaml"));
0130     RETURN_IF_TEST_FAILED();
0131 
0132     QHash<QString,QUrl> files = generator->fileUrls();
0133     QCOMPARE(files.size(), 1);
0134 
0135     QVERIFY(files.contains(QStringLiteral("Description")));
0136     QCOMPARE(files[QStringLiteral("Description")], baseUrl.resolved(QUrl(QStringLiteral("ClassName.yaml"))));
0137 }
0138 
0139 void TestTemplateClassGenerator::customOptions()
0140 {
0141     auto generator = loadTemplate(QStringLiteral("test_yaml"));
0142     RETURN_IF_TEST_FAILED();
0143     QCOMPARE(generator->sourceFileTemplate().hasCustomOptions(), false);
0144 
0145     generator = loadTemplate(QStringLiteral("test_options"));
0146     RETURN_IF_TEST_FAILED();
0147     QCOMPARE(generator->sourceFileTemplate().hasCustomOptions(), true);
0148 
0149     // test if option data loaded with all values in same order as in kcfg file
0150     struct ExpectedOption
0151     {
0152         const char* name; const char* label; const char* type; const char* value; QStringList values;
0153     };
0154     const struct {
0155         const char* name;
0156         QVector<ExpectedOption> expectedOptions;
0157     } expectedGroupDataList[] = {
0158         { "A Group", {
0159             {"bool_option",   "A Bool",     "Bool",   "true",   {}},
0160             {"string_option", "Zzz String", "String", "Test",   {}},
0161             {"enum_option",   "Bb Enum",    "Enum",   "Second", {
0162                 {QLatin1String("First")}, {QLatin1String("Second")}, {QLatin1String("Last")}
0163             }}
0164         }},
0165         { "Zzz Group", {
0166             {"z_option", "Z Bool", "Bool", "false", {}}
0167         }},
0168         { "Bb Group", {
0169             {"b_option", "B Bool", "Bool", "true",  {}}
0170         }}
0171     };
0172     const int expectedGroupDataCount = sizeof(expectedGroupDataList)/sizeof(expectedGroupDataList[0]);
0173     
0174     const auto customOptionGroups = generator->sourceFileTemplate().customOptions(generator->renderer());
0175 
0176     QCOMPARE(customOptionGroups.count(), expectedGroupDataCount);
0177     for (int i = 0; i < expectedGroupDataCount; ++i) {
0178         const auto& customOptionGroup = customOptionGroups[i];
0179         const auto& expectedGroupData = expectedGroupDataList[i];
0180 
0181         QCOMPARE(customOptionGroup.name, QString::fromLatin1(expectedGroupData.name));
0182         QCOMPARE(customOptionGroup.options.count(), expectedGroupData.expectedOptions.count());
0183         for (int j = 0; j < expectedGroupData.expectedOptions.count(); ++j) {
0184             const auto& customOption = customOptionGroup.options[j];
0185             const auto& expectedOptionData = expectedGroupData.expectedOptions[j];
0186 
0187             QCOMPARE(customOption.name, QString::fromLatin1(expectedOptionData.name));
0188             QCOMPARE(customOption.label, QString::fromLatin1(expectedOptionData.label));
0189             QCOMPARE(customOption.type, QString::fromLatin1(expectedOptionData.type));
0190             QCOMPARE(customOption.value.toString(), QString::fromLatin1(expectedOptionData.value));
0191             QCOMPARE(customOption.values, expectedOptionData.values);
0192         }
0193     }
0194 }
0195 
0196 void TestTemplateClassGenerator::templateVariablesCpp()
0197 {
0198     auto generator = loadTemplate(QStringLiteral("test_cpp"));
0199     RETURN_IF_TEST_FAILED();
0200     setLowercaseFileNames(generator);
0201 
0202     QVariantHash variables = generator->renderer()->variables();
0203     CHECK_TEMPLATE_VARIABLE(name, toString, QStringLiteral("ClassName"));
0204 
0205     CHECK_TEMPLATE_VARIABLE(output_file_header, toString, QStringLiteral("classname.h"));
0206     CHECK_TEMPLATE_VARIABLE(output_file_header_absolute, toString, baseUrl.resolved(QUrl(QStringLiteral("classname.h"))).toLocalFile());
0207 }
0208 
0209 void TestTemplateClassGenerator::templateVariablesYaml()
0210 {
0211     auto generator = loadTemplate(QStringLiteral("test_yaml"));
0212     RETURN_IF_TEST_FAILED();
0213     setLowercaseFileNames(generator);
0214 
0215     QVariantHash variables = generator->renderer()->variables();
0216     CHECK_TEMPLATE_VARIABLE(name, toString, QStringLiteral("ClassName"));
0217 
0218     CHECK_TEMPLATE_VARIABLE(output_file_description, toString, QStringLiteral("classname.yaml"));
0219     CHECK_TEMPLATE_VARIABLE(output_file_description_absolute, toString, baseUrl.resolved(QUrl(QStringLiteral("classname.yaml"))).toLocalFile());
0220 }
0221 
0222 void TestTemplateClassGenerator::codeDescription()
0223 {
0224     auto generator = loadTemplate(QStringLiteral("test_yaml"));
0225     RETURN_IF_TEST_FAILED();
0226 
0227     QVariantHash variables = generator->renderer()->variables();
0228 
0229     qDebug() << variables;
0230 
0231     QVERIFY(variables.contains(QStringLiteral("base_classes")));
0232     QVariantList inheritance = variables[QStringLiteral("base_classes")].toList();
0233     QCOMPARE(inheritance.size(), 1);
0234     QCOMPARE(inheritance.first().value<InheritanceDescription>().baseType, QStringLiteral("QObject"));
0235     QCOMPARE(inheritance.first().value<InheritanceDescription>().inheritanceMode, QStringLiteral("public"));
0236 
0237     QVERIFY(variables.contains(QStringLiteral("members")));
0238     QVariantList members = variables[QStringLiteral("members")].toList();
0239     QCOMPARE(members.size(), 3);
0240     QCOMPARE(members.first().value<VariableDescription>().type, QStringLiteral("QString"));
0241     QCOMPARE(members.first().value<VariableDescription>().name, QStringLiteral("name"));
0242 
0243     QVERIFY(variables.contains(QStringLiteral("functions")));
0244     QVariantList methods = variables[QStringLiteral("functions")].toList();
0245     QCOMPARE(methods.size(), 2);
0246     QCOMPARE(methods.first().value<FunctionDescription>().name, QStringLiteral("doSomething"));
0247     QCOMPARE(methods.first().value<FunctionDescription>().arguments.size(), 2);
0248     QCOMPARE(methods.first().value<FunctionDescription>().returnArguments.size(), 0);
0249     QCOMPARE(methods.last().value<FunctionDescription>().name, QStringLiteral("getSomeOtherNumber"));
0250     QCOMPARE(methods.last().value<FunctionDescription>().arguments.size(), 0);
0251     QCOMPARE(methods.last().value<FunctionDescription>().returnArguments.size(), 1);
0252 }
0253 
0254 void TestTemplateClassGenerator::generate()
0255 {
0256     auto generator = loadTemplate(QStringLiteral("test_cpp"));
0257     RETURN_IF_TEST_FAILED();
0258 
0259     DocumentChangeSet changes = generator->generate();
0260     DocumentChangeSet::ChangeResult result = changes.applyAllChanges();
0261     QVERIFY(result.m_success);
0262 
0263     QDir dir(baseUrl.toLocalFile());
0264     QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
0265     QCOMPARE(entries.size(), 2);
0266 }
0267 
0268 void TestTemplateClassGenerator::cppOutput()
0269 {
0270     auto generator = loadTemplate(QStringLiteral("test_cpp"));
0271     RETURN_IF_TEST_FAILED();
0272     setLowercaseFileNames(generator);
0273 
0274     DocumentChangeSet changes = generator->generate();
0275     changes.setFormatPolicy(DocumentChangeSet::NoAutoFormat);
0276     changes.applyAllChanges();
0277 
0278     QFile header(baseUrl.resolved(QUrl(QStringLiteral("classname.h"))).toLocalFile());
0279     QVERIFY(header.open(QIODevice::ReadOnly));
0280 
0281     QFile testHeader(QStringLiteral(CODEGEN_TESTS_EXPECTED_DIR "/classname.h"));
0282     testHeader.open(QIODevice::ReadOnly);
0283     COMPARE_FILES(header, testHeader);
0284 
0285     QFile implementation(baseUrl.resolved(QUrl(QStringLiteral("classname.cpp"))).toLocalFile());
0286     QVERIFY(implementation.open(QIODevice::ReadOnly));
0287 
0288     QFile testImplementation(QStringLiteral(CODEGEN_TESTS_EXPECTED_DIR "/classname.cpp"));
0289     testImplementation.open(QIODevice::ReadOnly);
0290     COMPARE_FILES(implementation, testImplementation);
0291 }
0292 
0293 void TestTemplateClassGenerator::yamlOutput()
0294 {
0295     auto generator = loadTemplate(QStringLiteral("test_yaml"));
0296     RETURN_IF_TEST_FAILED();
0297     setLowercaseFileNames(generator);
0298     generator->generate().applyAllChanges();
0299 
0300     QFile yaml(baseUrl.resolved(QUrl(QStringLiteral("classname.yaml"))).toLocalFile());
0301     QVERIFY(yaml.open(QIODevice::ReadOnly));
0302 
0303     QFile testYaml(QStringLiteral(CODEGEN_TESTS_EXPECTED_DIR "/classname.yaml"));
0304     testYaml.open(QIODevice::ReadOnly);
0305     COMPARE_FILES(yaml, testYaml);
0306 }
0307 
0308 std::unique_ptr<TemplateClassGenerator> TestTemplateClassGenerator::loadTemplate(const QString& name)
0309 {
0310     QDir dir(baseUrl.toLocalFile());
0311     const auto files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
0312     for (const QString& fileName : files) {
0313         dir.remove(fileName);
0314     }
0315 
0316     auto generator = std::make_unique<TemplateClassGenerator>(baseUrl);
0317 
0318     QString tplDescription = QStringLiteral(CODEGEN_DATA_DIR) + "/kdevcodegentest/templates/" + name + "/" + name + ".desktop";
0319     QVERIFY_RETURN(!tplDescription.isEmpty(), nullptr);
0320 
0321     SourceFileTemplate tpl;
0322     tpl.addAdditionalSearchLocation(QStringLiteral(CODEGEN_TESTS_DATA_DIR) + "/kdevcodegentest/templates/");
0323     tpl.setTemplateDescription(tplDescription);
0324     QVERIFY_RETURN(tpl.isValid(), nullptr);
0325 
0326     generator->setTemplateDescription(tpl);
0327     generator->setDescription(description);
0328     generator->setIdentifier(QStringLiteral("ClassName"));
0329     generator->addBaseClass(QStringLiteral("public QObject"));
0330     generator->setLicense(QStringLiteral("This is just a test.\nYou may do with it as you please."));
0331     return generator;
0332 }
0333 
0334 QTEST_GUILESS_MAIN(TestTemplateClassGenerator)
0335 
0336 #include "moc_test_templateclassgenerator.cpp"