CMakeGUITest.cxx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "CMakeGUITest.h"
  4. #include "QCMake.h"
  5. #include <QApplication>
  6. #include <QEventLoop>
  7. #include <QFile>
  8. #include <QJsonArray>
  9. #include <QJsonDocument>
  10. #include <QJsonObject>
  11. #include <QMessageBox>
  12. #include <QSettings>
  13. #include <QString>
  14. #include <QStringList>
  15. #include <QTimer>
  16. #include <QtGlobal>
  17. #include <QtTest>
  18. #include "CMakeSetupDialog.h"
  19. #include "CatchShow.h"
  20. #include "FirstConfigure.h"
  21. using WindowSetupHelper = std::function<void(CMakeSetupDialog*)>;
  22. Q_DECLARE_METATYPE(WindowSetupHelper)
  23. namespace {
  24. void loopSleep(int msecs = 500)
  25. {
  26. QEventLoop loop;
  27. QTimer::singleShot(msecs, &loop, &QEventLoop::quit);
  28. loop.exec();
  29. }
  30. }
  31. CMakeGUITest::CMakeGUITest(CMakeSetupDialog* window, QObject* parent)
  32. : QObject(parent)
  33. , m_window(window)
  34. {
  35. }
  36. void CMakeGUITest::tryConfigure(int expectedResult, int timeout)
  37. {
  38. auto* cmake = this->m_window->findChild<QCMakeThread*>()->cmakeInstance();
  39. bool done = false;
  40. CatchShow catchConfigure;
  41. catchConfigure.setCallback<FirstConfigure>([&done](FirstConfigure* dialog) {
  42. if (done) {
  43. return;
  44. }
  45. done = true;
  46. dialog->findChild<StartCompilerSetup*>()->setCurrentGenerator(
  47. CMAKE_GENERATOR);
  48. dialog->accept();
  49. });
  50. CatchShow catchMessages;
  51. catchMessages.setCallback<QMessageBox>([](QMessageBox* box) {
  52. if (box->text().contains("Build directory does not exist")) {
  53. box->accept();
  54. }
  55. if (box->text().contains("Error in configuration process")) {
  56. box->accept();
  57. }
  58. });
  59. QSignalSpy configureDoneSpy(cmake, &QCMake::configureDone);
  60. QVERIFY(configureDoneSpy.isValid());
  61. QMetaObject::invokeMethod(
  62. this->m_window, [this]() { this->m_window->ConfigureButton->click(); },
  63. Qt::QueuedConnection);
  64. QVERIFY(configureDoneSpy.wait(timeout));
  65. QCOMPARE(configureDoneSpy, { { expectedResult } });
  66. }
  67. void CMakeGUITest::sourceBinaryArgs()
  68. {
  69. QFETCH(QString, sourceDir);
  70. QFETCH(QString, binaryDir);
  71. // Wait a bit for everything to update
  72. loopSleep();
  73. QCOMPARE(this->m_window->SourceDirectory->text(), sourceDir);
  74. QCOMPARE(this->m_window->BinaryDirectory->currentText(), binaryDir);
  75. }
  76. void CMakeGUITest::sourceBinaryArgs_data()
  77. {
  78. QTest::addColumn<QString>("sourceDir");
  79. QTest::addColumn<QString>("binaryDir");
  80. QTest::newRow("sourceAndBinaryDir")
  81. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-sourceAndBinaryDir/src"
  82. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-sourceAndBinaryDir/build";
  83. QTest::newRow("sourceAndBinaryDirRelative") << CMakeGUITest_BINARY_DIR
  84. "/sourceBinaryArgs-sourceAndBinaryDirRelative/src"
  85. << CMakeGUITest_BINARY_DIR
  86. "/sourceBinaryArgs-sourceAndBinaryDirRelative/build";
  87. QTest::newRow("sourceDir")
  88. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-sourceDir/src"
  89. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-sourceDir";
  90. QTest::newRow("binaryDir")
  91. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-binaryDir/src"
  92. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-binaryDir/build";
  93. QTest::newRow("noExist") << ""
  94. << "";
  95. QTest::newRow("noExistConfig")
  96. << ""
  97. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-noExistConfig/oldbuild";
  98. QTest::newRow("noExistConfigExists")
  99. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-noExistConfigExists/src"
  100. << CMakeGUITest_BINARY_DIR "/sourceBinaryArgs-noExistConfigExists/build";
  101. }
  102. void CMakeGUITest::simpleConfigure()
  103. {
  104. QFETCH(QString, sourceDir);
  105. QFETCH(QString, binaryDir);
  106. QFETCH(int, expectedResult);
  107. this->m_window->SourceDirectory->setText(sourceDir);
  108. this->m_window->BinaryDirectory->setCurrentText(binaryDir);
  109. // Wait a bit for everything to update
  110. loopSleep();
  111. this->tryConfigure(expectedResult, 1000);
  112. }
  113. void CMakeGUITest::simpleConfigure_data()
  114. {
  115. QTest::addColumn<QString>("sourceDir");
  116. QTest::addColumn<QString>("binaryDir");
  117. QTest::addColumn<int>("expectedResult");
  118. QTest::newRow("success") << CMakeGUITest_BINARY_DIR
  119. "/simpleConfigure-success/src"
  120. << CMakeGUITest_BINARY_DIR
  121. "/simpleConfigure-success/build"
  122. << 0;
  123. QTest::newRow("fail") << CMakeGUITest_BINARY_DIR "/simpleConfigure-fail/src"
  124. << CMakeGUITest_BINARY_DIR
  125. "/simpleConfigure-fail/build"
  126. << -1;
  127. }
  128. void CMakeGUITest::environment()
  129. {
  130. auto* cmake = this->m_window->findChild<QCMakeThread*>()->cmakeInstance();
  131. this->m_window->SourceDirectory->setText(CMakeGUITest_BINARY_DIR
  132. "/environment/src");
  133. this->m_window->BinaryDirectory->setCurrentText(CMakeGUITest_BINARY_DIR
  134. "/environment/build");
  135. // We are already testing EnvironmentDialog, so just trust that it's
  136. // connected correctly and modify the environment directly.
  137. auto env = cmake->environment();
  138. env.insert("ADDED_VARIABLE", "Added variable");
  139. env.insert("CHANGED_VARIABLE", "Changed variable");
  140. env.remove("REMOVED_VARIABLE");
  141. cmake->setEnvironment(env);
  142. // Wait a bit for everything to update
  143. loopSleep();
  144. this->tryConfigure();
  145. auto penv = QProcessEnvironment::systemEnvironment();
  146. QVERIFY(!penv.contains("ADDED_VARIABLE"));
  147. QCOMPARE(penv.value("KEPT_VARIABLE"), "Kept variable");
  148. QCOMPARE(penv.value("CHANGED_VARIABLE"), "This variable will be changed");
  149. QCOMPARE(penv.value("REMOVED_VARIABLE"), "Removed variable");
  150. }
  151. void CMakeGUITest::presetArg()
  152. {
  153. QFETCH(WindowSetupHelper, setupFunction);
  154. QFETCH(QString, presetName);
  155. QFETCH(QString, sourceDir);
  156. QFETCH(QString, binaryDir);
  157. QFETCH(QCMakePropertyList, properties);
  158. if (setupFunction) {
  159. setupFunction(this->m_window);
  160. }
  161. // Wait a bit for everything to update
  162. loopSleep();
  163. QCOMPARE(this->m_window->Preset->presetName(), presetName);
  164. QCOMPARE(this->m_window->SourceDirectory->text(), sourceDir);
  165. QCOMPARE(this->m_window->BinaryDirectory->currentText(), binaryDir);
  166. auto actualProperties =
  167. this->m_window->CacheValues->cacheModel()->properties();
  168. QCOMPARE(actualProperties.size(), properties.size());
  169. for (int i = 0; i < actualProperties.size(); ++i) {
  170. // operator==() only compares Key, we need to compare Value and Type too
  171. QCOMPARE(actualProperties[i].Key, properties[i].Key);
  172. QCOMPARE(actualProperties[i].Value, properties[i].Value);
  173. QCOMPARE(actualProperties[i].Type, properties[i].Type);
  174. }
  175. }
  176. namespace {
  177. QCMakePropertyList makePresetProperties(const QString& name)
  178. {
  179. return QCMakePropertyList{
  180. QCMakeProperty{
  181. /*Key=*/"FALSE_VARIABLE",
  182. /*Value=*/false,
  183. /*Strings=*/{},
  184. /*Help=*/"",
  185. /*Type=*/QCMakeProperty::BOOL,
  186. /*Advanced=*/false,
  187. },
  188. QCMakeProperty{
  189. /*Key=*/"FILEPATH_VARIABLE",
  190. /*Value=*/
  191. QString::fromLocal8Bit(CMakeGUITest_BINARY_DIR "/%1/src/CMakeLists.txt")
  192. .arg(name),
  193. /*Strings=*/{},
  194. /*Help=*/"",
  195. /*Type=*/QCMakeProperty::FILEPATH,
  196. /*Advanced=*/false,
  197. },
  198. QCMakeProperty{
  199. /*Key=*/"ON_VARIABLE",
  200. /*Value=*/true,
  201. /*Strings=*/{},
  202. /*Help=*/"",
  203. /*Type=*/QCMakeProperty::BOOL,
  204. /*Advanced=*/false,
  205. },
  206. QCMakeProperty{
  207. /*Key=*/"PATH_VARIABLE",
  208. /*Value=*/
  209. QString::fromLocal8Bit(CMakeGUITest_BINARY_DIR "/%1/src").arg(name),
  210. /*Strings=*/{},
  211. /*Help=*/"",
  212. /*Type=*/QCMakeProperty::PATH,
  213. /*Advanced=*/false,
  214. },
  215. QCMakeProperty{
  216. /*Key=*/"STRING_VARIABLE",
  217. /*Value=*/"String value",
  218. /*Strings=*/{},
  219. /*Help=*/"",
  220. /*Type=*/QCMakeProperty::STRING,
  221. /*Advanced=*/false,
  222. },
  223. QCMakeProperty{
  224. /*Key=*/"UNINITIALIZED_VARIABLE",
  225. /*Value=*/"Uninitialized value",
  226. /*Strings=*/{},
  227. /*Help=*/"",
  228. /*Type=*/QCMakeProperty::STRING,
  229. /*Advanced=*/false,
  230. },
  231. };
  232. }
  233. }
  234. void CMakeGUITest::presetArg_data()
  235. {
  236. QTest::addColumn<WindowSetupHelper>("setupFunction");
  237. QTest::addColumn<QString>("presetName");
  238. QTest::addColumn<QString>("sourceDir");
  239. QTest::addColumn<QString>("binaryDir");
  240. QTest::addColumn<QCMakePropertyList>("properties");
  241. QTest::newRow("preset") << WindowSetupHelper{} << "ninja"
  242. << CMakeGUITest_BINARY_DIR "/presetArg-preset/src"
  243. << CMakeGUITest_BINARY_DIR
  244. "/presetArg-preset/src/build"
  245. << makePresetProperties("presetArg-preset");
  246. QTest::newRow("presetBinary")
  247. << WindowSetupHelper{} << "ninja"
  248. << CMakeGUITest_BINARY_DIR "/presetArg-presetBinary/src"
  249. << CMakeGUITest_BINARY_DIR "/presetArg-presetBinary/build"
  250. << makePresetProperties("presetArg-presetBinary");
  251. QTest::newRow("presetBinaryChange")
  252. << WindowSetupHelper{ [](CMakeSetupDialog* window) {
  253. loopSleep();
  254. window->Preset->setPresetName("ninja2");
  255. } }
  256. << "ninja2" << CMakeGUITest_BINARY_DIR "/presetArg-presetBinaryChange/src"
  257. << CMakeGUITest_BINARY_DIR "/presetArg-presetBinaryChange/src/build"
  258. << makePresetProperties("presetArg-presetBinaryChange");
  259. QTest::newRow("noPresetBinaryChange")
  260. << WindowSetupHelper{ [](CMakeSetupDialog* window) {
  261. loopSleep();
  262. window->Preset->setPresetName("ninja");
  263. } }
  264. << "ninja" << CMakeGUITest_BINARY_DIR "/presetArg-noPresetBinaryChange/src"
  265. << CMakeGUITest_BINARY_DIR "/presetArg-noPresetBinaryChange/src/build"
  266. << makePresetProperties("presetArg-noPresetBinaryChange");
  267. QTest::newRow("presetConfigExists")
  268. << WindowSetupHelper{} << "ninja"
  269. << CMakeGUITest_BINARY_DIR "/presetArg-presetConfigExists/src"
  270. << CMakeGUITest_BINARY_DIR "/presetArg-presetConfigExists/src/build"
  271. << makePresetProperties("presetArg-presetConfigExists");
  272. QTest::newRow("noExist") << WindowSetupHelper{} << QString{}
  273. << CMakeGUITest_BINARY_DIR "/presetArg-noExist/src"
  274. << "" << QCMakePropertyList{};
  275. }
  276. namespace {
  277. void writePresets(const QString& buildDir, const QStringList& names)
  278. {
  279. QJsonArray presets{
  280. QJsonObject{
  281. { "name", "base" },
  282. { "generator", "Ninja" },
  283. { "binaryDir",
  284. QString::fromLocal8Bit("${sourceDir}/%1/${presetName}")
  285. .arg(buildDir) },
  286. { "hidden", true },
  287. },
  288. };
  289. for (auto const& name : names) {
  290. presets.append(QJsonObject{
  291. { "name", name },
  292. { "inherits", QJsonArray{ "base" } },
  293. });
  294. }
  295. QJsonDocument doc{ QJsonObject{
  296. { "version", 1 },
  297. { "configurePresets", presets },
  298. } };
  299. QFile presetsFile(CMakeGUITest_BINARY_DIR
  300. "/changingPresets/src/CMakePresets.json");
  301. bool open = presetsFile.open(QIODevice::WriteOnly);
  302. Q_ASSERT(open);
  303. presetsFile.write(doc.toJson());
  304. }
  305. }
  306. void CMakeGUITest::changingPresets()
  307. {
  308. QDir::root().mkpath(CMakeGUITest_BINARY_DIR "/changingPresets/src");
  309. this->m_window->SourceDirectory->setText(CMakeGUITest_BINARY_DIR
  310. "/changingPresets/src");
  311. loopSleep();
  312. QCOMPARE(this->m_window->Preset->presetName(), QString{});
  313. QCOMPARE(this->m_window->Preset->presets().size(), 0);
  314. QCOMPARE(this->m_window->BinaryDirectory->currentText(), "");
  315. QCOMPARE(this->m_window->Preset->isHidden(), true);
  316. QCOMPARE(this->m_window->PresetLabel->isHidden(), true);
  317. writePresets("build1", { "preset" });
  318. loopSleep(1500);
  319. QCOMPARE(this->m_window->Preset->presetName(), QString{});
  320. QCOMPARE(this->m_window->Preset->presets().size(), 1);
  321. QCOMPARE(this->m_window->BinaryDirectory->currentText(), "");
  322. QCOMPARE(this->m_window->Preset->isHidden(), false);
  323. QCOMPARE(this->m_window->PresetLabel->isHidden(), false);
  324. this->m_window->Preset->setPresetName("preset");
  325. loopSleep();
  326. QCOMPARE(this->m_window->Preset->presetName(), "preset");
  327. QCOMPARE(this->m_window->Preset->presets().size(), 1);
  328. QCOMPARE(this->m_window->BinaryDirectory->currentText(),
  329. CMakeGUITest_BINARY_DIR "/changingPresets/src/build1/preset");
  330. QCOMPARE(this->m_window->Preset->isHidden(), false);
  331. QCOMPARE(this->m_window->PresetLabel->isHidden(), false);
  332. writePresets("build2", { "preset2", "preset" });
  333. loopSleep(1500);
  334. QCOMPARE(this->m_window->Preset->presetName(), "preset");
  335. QCOMPARE(this->m_window->Preset->presets().size(), 2);
  336. QCOMPARE(this->m_window->BinaryDirectory->currentText(),
  337. CMakeGUITest_BINARY_DIR "/changingPresets/src/build1/preset");
  338. QCOMPARE(this->m_window->Preset->isHidden(), false);
  339. QCOMPARE(this->m_window->PresetLabel->isHidden(), false);
  340. writePresets("build3", { "preset2" });
  341. loopSleep(1500);
  342. QCOMPARE(this->m_window->Preset->presetName(), QString{});
  343. QCOMPARE(this->m_window->Preset->presets().size(), 1);
  344. QCOMPARE(this->m_window->BinaryDirectory->currentText(),
  345. CMakeGUITest_BINARY_DIR "/changingPresets/src/build1/preset");
  346. QCOMPARE(this->m_window->Preset->isHidden(), false);
  347. QCOMPARE(this->m_window->PresetLabel->isHidden(), false);
  348. this->m_window->Preset->setPresetName("preset2");
  349. loopSleep();
  350. QCOMPARE(this->m_window->Preset->presetName(), "preset2");
  351. QCOMPARE(this->m_window->Preset->presets().size(), 1);
  352. QCOMPARE(this->m_window->BinaryDirectory->currentText(),
  353. CMakeGUITest_BINARY_DIR "/changingPresets/src/build3/preset2");
  354. QCOMPARE(this->m_window->Preset->isHidden(), false);
  355. QCOMPARE(this->m_window->PresetLabel->isHidden(), false);
  356. QDir::root().mkpath(CMakeGUITest_BINARY_DIR "/changingPresets/src2");
  357. QFile::copy(CMakeGUITest_BINARY_DIR "/changingPresets/src/CMakePresets.json",
  358. CMakeGUITest_BINARY_DIR
  359. "/changingPresets/src2/CMakePresets.json");
  360. this->m_window->SourceDirectory->setText(CMakeGUITest_BINARY_DIR
  361. "/changingPresets/src2");
  362. loopSleep();
  363. QCOMPARE(this->m_window->Preset->presetName(), QString{});
  364. QCOMPARE(this->m_window->Preset->presets().size(), 1);
  365. QCOMPARE(this->m_window->BinaryDirectory->currentText(),
  366. CMakeGUITest_BINARY_DIR "/changingPresets/src/build3/preset2");
  367. QCOMPARE(this->m_window->Preset->isHidden(), false);
  368. QCOMPARE(this->m_window->PresetLabel->isHidden(), false);
  369. this->m_window->Preset->setPresetName("preset2");
  370. loopSleep();
  371. QCOMPARE(this->m_window->Preset->presetName(), "preset2");
  372. QCOMPARE(this->m_window->Preset->presets().size(), 1);
  373. QCOMPARE(this->m_window->BinaryDirectory->currentText(),
  374. CMakeGUITest_BINARY_DIR "/changingPresets/src2/build3/preset2");
  375. QCOMPARE(this->m_window->Preset->isHidden(), false);
  376. QCOMPARE(this->m_window->PresetLabel->isHidden(), false);
  377. QFile(CMakeGUITest_BINARY_DIR "/changingPresets/src2/CMakePresets.json")
  378. .remove();
  379. loopSleep(1500);
  380. QCOMPARE(this->m_window->Preset->presetName(), QString{});
  381. QCOMPARE(this->m_window->Preset->presets().size(), 0);
  382. QCOMPARE(this->m_window->BinaryDirectory->currentText(),
  383. CMakeGUITest_BINARY_DIR "/changingPresets/src2/build3/preset2");
  384. QCOMPARE(this->m_window->Preset->isHidden(), true);
  385. QCOMPARE(this->m_window->PresetLabel->isHidden(), true);
  386. }
  387. void SetupDefaultQSettings()
  388. {
  389. QSettings::setDefaultFormat(QSettings::IniFormat);
  390. QSettings::setPath(QSettings::IniFormat, QSettings::UserScope,
  391. QString::fromLocal8Bit(qgetenv("CMake_GUI_CONFIG_DIR")));
  392. }
  393. int CMakeGUIExec(CMakeSetupDialog* window)
  394. {
  395. auto nameArray = qgetenv("CMake_GUI_TEST_NAME");
  396. auto name = QString::fromLocal8Bit(nameArray);
  397. if (name.isEmpty()) {
  398. return QApplication::exec();
  399. }
  400. QStringList args{ "CMakeGUITest", name };
  401. CMakeGUITest obj(window);
  402. return QTest::qExec(&obj, args);
  403. }