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"