File indexing completed on 2024-05-26 04:38:56

0001 /*
0002     This file is part of KDevelop
0003 
0004     Copyright 2008 Evgeniy Ivanov <powerfox@kde.ru>
0005     Copyright 2009 Fabian Wiesel <fabian.wiesel@fu-berlin.de>
0006     Copyright 2017 Sergey Kalinichev <kalinichev.so.0@gmail.com>
0007 
0008     This library is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU Library General Public
0010     License as published by the Free Software Foundation; either
0011     version 2 of the License, or (at your option) any later version.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Library General Public License for more details.
0017 
0018     You should have received a copy of the GNU Library General Public License
0019     along with this library; see the file COPYING.LIB.  If not, write to
0020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021     Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "test_mercurial.h"
0025 
0026 #include <QTest>
0027 #include <QSignalSpy>
0028 #include <tests/testcore.h>
0029 #include <tests/autotestshell.h>
0030 
0031 #include <QScopedPointer>
0032 #include <QUrl>
0033 
0034 #include <KIO/DeleteJob>
0035 
0036 #include <vcs/dvcs/dvcsjob.h>
0037 #include <vcs/vcsannotation.h>
0038 #include <vcs/vcslocation.h>
0039 
0040 #include "../mercurialplugin.h"
0041 #include "../mercurialannotatejob.h"
0042 #include "../models/mercurialheadsmodel.h"
0043 #include "debug.h"
0044 
0045 using namespace KDevelop;
0046 
0047 namespace
0048 {
0049 const QString tempDir = QDir::tempPath();
0050 const QString MercurialTestDir1("kdevMercurial_testdir");
0051 const QString mercurialTest_BaseDir(tempDir + "/kdevMercurial_testdir/");
0052 const QString mercurialTest_BaseDir2(tempDir + "/kdevMercurial_testdir2/");
0053 const QString mercurialRepo(mercurialTest_BaseDir + ".hg");
0054 const QString mercurialSrcDir(mercurialTest_BaseDir + "src/");
0055 const QString mercurialTest_FileName("testfile");
0056 const QString mercurialTest_FileName2("foo");
0057 const QString mercurialTest_FileName3("bar");
0058 
0059 void verifyJobSucceed(VcsJob* job)
0060 {
0061     QVERIFY(job);
0062     QVERIFY(job->exec());
0063     QVERIFY(job->status() == KDevelop::VcsJob::JobSucceeded);
0064 }
0065 
0066 void writeToFile(const QString& path, const QString& content, QIODevice::OpenModeFlag mode = QIODevice::WriteOnly)
0067 {
0068     QFile f(path);
0069     QVERIFY(f.open(mode));
0070     QTextStream input(&f);
0071     input << content << endl;
0072 }
0073 
0074 }
0075 
0076 void MercurialTest::initTestCase()
0077 {
0078     AutoTestShell::init({"kdevmercurial"});
0079     m_testCore = new KDevelop::TestCore();
0080     m_testCore->initialize(KDevelop::Core::NoUi);
0081 
0082     m_proxy = new MercurialPlugin(m_testCore);
0083 }
0084 
0085 void MercurialTest::cleanupTestCase()
0086 {
0087     delete m_proxy;
0088 
0089     removeTempDirs();
0090 }
0091 
0092 void MercurialTest::init()
0093 {
0094     QDir tmpdir(tempDir);
0095     tmpdir.mkdir(mercurialTest_BaseDir);
0096     tmpdir.mkdir(mercurialSrcDir);
0097     tmpdir.mkdir(mercurialTest_BaseDir2);
0098 }
0099 
0100 void MercurialTest::cleanup()
0101 {
0102     removeTempDirs();
0103 }
0104 
0105 void MercurialTest::repoInit()
0106 {
0107     qCDebug(PLUGIN_MERCURIAL) << "Trying to init repo";
0108     // create the local repository
0109     VcsJob *j = m_proxy->init(QUrl::fromLocalFile(mercurialTest_BaseDir));
0110     verifyJobSucceed(j);
0111     QVERIFY(QFileInfo(mercurialRepo).exists());
0112 
0113     QVERIFY(m_proxy->isValidDirectory(QUrl::fromLocalFile(mercurialTest_BaseDir)));
0114     QVERIFY(!m_proxy->isValidDirectory(QUrl::fromLocalFile("/tmp")));
0115 }
0116 
0117 void MercurialTest::addFiles()
0118 {
0119     qCDebug(PLUGIN_MERCURIAL) << "Adding files to the repo";
0120 
0121     writeToFile(mercurialTest_BaseDir + mercurialTest_FileName, "commit 0 content");
0122     writeToFile(mercurialTest_BaseDir + mercurialTest_FileName2, "commit 0 content, foo");
0123 
0124     VcsJob *j = m_proxy->status({QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0125     verifyJobSucceed(j);
0126     auto statusResults = j->fetchResults().toList();
0127     QCOMPARE(statusResults.size(), 2);
0128     auto status = statusResults[0].value<VcsStatusInfo>();
0129     QCOMPARE(status.url(), QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName2));
0130     QCOMPARE(status.state(), VcsStatusInfo::ItemUnknown);
0131     status = statusResults[1].value<VcsStatusInfo>();
0132     QCOMPARE(status.url(), QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName));
0133     QCOMPARE(status.state(), VcsStatusInfo::ItemUnknown);
0134 
0135     j = m_proxy->add({QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::NonRecursive);
0136     QVERIFY(!j);
0137 
0138     // /tmp/kdevMercurial_testdir/ and kdevMercurial_testdir
0139     //add always should use aboslute path to the any directory of the repository, let's check:
0140     j = m_proxy->add({QUrl::fromLocalFile(mercurialTest_BaseDir), QUrl::fromLocalFile(MercurialTestDir1)}, KDevelop::IBasicVersionControl::Recursive);
0141     verifyJobSucceed(j);
0142 
0143     // /tmp/kdevMercurial_testdir/ and testfile
0144     j = m_proxy->add({QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName)}, KDevelop::IBasicVersionControl::Recursive);
0145     verifyJobSucceed(j);
0146 
0147     writeToFile(mercurialSrcDir + mercurialTest_FileName3, "commit 0 content, bar");
0148 
0149     j = m_proxy->status({QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0150     verifyJobSucceed(j);
0151     statusResults = j->fetchResults().toList();
0152     QCOMPARE(statusResults.size(), 3);
0153     status = statusResults[0].value<VcsStatusInfo>();
0154     QCOMPARE(status.url(), QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName2));
0155     QCOMPARE(status.state(), VcsStatusInfo::ItemAdded);
0156     status = statusResults[1].value<VcsStatusInfo>();
0157     QCOMPARE(status.url(), QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName));
0158     QCOMPARE(status.state(), VcsStatusInfo::ItemAdded);
0159     status = statusResults[2].value<VcsStatusInfo>();
0160     QCOMPARE(status.url(), QUrl::fromLocalFile(mercurialSrcDir + mercurialTest_FileName3));
0161     QCOMPARE(status.state(), VcsStatusInfo::ItemUnknown);
0162 
0163     // repository path without trailing slash and a file in a parent directory
0164     // /tmp/repo  and /tmp/repo/src/bar
0165     j = m_proxy->add({QUrl::fromLocalFile(mercurialSrcDir + mercurialTest_FileName3)});
0166     verifyJobSucceed(j);
0167 
0168     // let's use absolute path, because it's used in ContextMenus
0169     j = m_proxy->add({QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName2)}, KDevelop::IBasicVersionControl::Recursive);
0170     verifyJobSucceed(j);
0171 
0172     //Now let's create several files and try "hg add file1 file2 file3"
0173     writeToFile(mercurialTest_BaseDir + "file1", "file1");
0174     writeToFile(mercurialTest_BaseDir + "file2", "file2");
0175 
0176     j = m_proxy->add({QUrl::fromLocalFile(mercurialTest_BaseDir + "file1"), QUrl::fromLocalFile(mercurialTest_BaseDir + "file2")}, KDevelop::IBasicVersionControl::Recursive);
0177     verifyJobSucceed(j);
0178 }
0179 
0180 void MercurialTest::commitFiles()
0181 {
0182     qCDebug(PLUGIN_MERCURIAL) << "Committing...";
0183 
0184     VcsJob *j = m_proxy->commit(QString("commit 0"), {QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0185     verifyJobSucceed(j);
0186 
0187     j = m_proxy->status({QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0188     verifyJobSucceed(j);
0189 
0190     // Test the results of the "mercurial add"
0191     DVcsJob *jobLs = new DVcsJob(mercurialTest_BaseDir, nullptr);
0192     *jobLs << "hg" << "stat" << "-q" << "-c" << "-n";
0193     verifyJobSucceed(jobLs);
0194 
0195     QStringList files = jobLs->output().split("\n");
0196     QVERIFY(files.contains(mercurialTest_FileName));
0197     QVERIFY(files.contains(mercurialTest_FileName2));
0198     QVERIFY(files.contains("src/" + mercurialTest_FileName3));
0199 
0200     qCDebug(PLUGIN_MERCURIAL) << "Committing one more time";
0201 
0202     // let's try to change the file and test "hg commit -a"
0203     writeToFile(mercurialTest_BaseDir + mercurialTest_FileName, "commit 1 content", QIODevice::Append);
0204 
0205     j = m_proxy->add({QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName)}, KDevelop::IBasicVersionControl::Recursive);
0206     verifyJobSucceed(j);
0207 
0208     j = m_proxy->commit(QString("commit 1"), {QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0209     verifyJobSucceed(j);
0210 }
0211 
0212 void MercurialTest::cloneRepository()
0213 {
0214     // make job that clones the local repository, created in the previous test
0215     VcsJob *j = m_proxy->createWorkingCopy(VcsLocation(mercurialTest_BaseDir), QUrl::fromLocalFile(mercurialTest_BaseDir2));
0216     verifyJobSucceed(j);
0217     QVERIFY(QFileInfo(QString(mercurialTest_BaseDir2 + "/.hg/")).exists());
0218 }
0219 
0220 void MercurialTest::testInit()
0221 {
0222     repoInit();
0223 }
0224 
0225 void MercurialTest::testAdd()
0226 {
0227     repoInit();
0228     addFiles();
0229 }
0230 
0231 void MercurialTest::testCommit()
0232 {
0233     repoInit();
0234     addFiles();
0235     commitFiles();
0236 }
0237 
0238 void MercurialTest::testBranching()
0239 {
0240     repoInit();
0241     addFiles();
0242     commitFiles();
0243 
0244     auto j = m_proxy->branches(QUrl::fromLocalFile(mercurialTest_BaseDir));
0245     verifyJobSucceed(j);
0246     auto branches = j->fetchResults().toList();
0247     QCOMPARE(branches.size(), 1);
0248     QVERIFY(branches.first().toString().startsWith(QStringLiteral("default")));
0249 
0250     // add branch
0251     j = m_proxy->branch(QUrl::fromLocalFile(mercurialTest_BaseDir), VcsRevision::createSpecialRevision(VcsRevision::Head), "test");
0252     verifyJobSucceed(j);
0253     j = m_proxy->commit(QString("commit 0"), {QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0254     verifyJobSucceed(j);
0255     j = m_proxy->branches(QUrl::fromLocalFile(mercurialTest_BaseDir));
0256     verifyJobSucceed(j);
0257     branches = j->fetchResults().toList();
0258     std::sort(branches.begin(), branches.end());
0259     QCOMPARE(branches.size(), 2);
0260     QVERIFY(branches.first().toString().startsWith(QStringLiteral("default")));
0261     QVERIFY(branches.last().toString().startsWith(QStringLiteral("test")));
0262 
0263     // switch branch
0264     j = m_proxy->currentBranch(QUrl::fromLocalFile(mercurialTest_BaseDir));
0265     verifyJobSucceed(j);
0266     branches = j->fetchResults().toList();
0267     QCOMPARE(branches.size(), 1);
0268     QVERIFY(branches.first().toString().startsWith(QStringLiteral("test")));
0269     j = m_proxy->switchBranch(QUrl::fromLocalFile(mercurialTest_BaseDir), QStringLiteral("default"));
0270     verifyJobSucceed(j);
0271     j = m_proxy->currentBranch(QUrl::fromLocalFile(mercurialTest_BaseDir));
0272     verifyJobSucceed(j);
0273     branches = j->fetchResults().toList();
0274     QCOMPARE(branches.size(), 1);
0275     QVERIFY(branches.first().toString().startsWith(QStringLiteral("default")));
0276 
0277     // commit + merge
0278     writeToFile(mercurialTest_BaseDir + mercurialTest_FileName, "commit 2 content");
0279     j = m_proxy->commit(QString("commit 2"), {QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0280     verifyJobSucceed(j);
0281     j = m_proxy->switchBranch(QUrl::fromLocalFile(mercurialTest_BaseDir), QStringLiteral("test"));
0282     verifyJobSucceed(j);
0283     j = m_proxy->mergeBranch(QUrl::fromLocalFile(mercurialTest_BaseDir), QStringLiteral("default"));
0284     verifyJobSucceed(j);
0285     j = m_proxy->commit(QString("Merge default"), {QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0286     verifyJobSucceed(j);
0287     auto commits = m_proxy->allCommits(mercurialTest_BaseDir);
0288     QCOMPARE(commits.count(), 5);
0289     QCOMPARE(commits[0].log(), QString("Merge default"));
0290     QCOMPARE(commits[0].parents().size(), 2);
0291 }
0292 
0293 void MercurialTest::testRevisionHistory()
0294 {
0295     repoInit();
0296     addFiles();
0297     commitFiles();
0298 
0299     const auto commits = m_proxy->allCommits(mercurialTest_BaseDir);
0300     QCOMPARE(commits.count(), 2);
0301     QCOMPARE(commits[0].parents().size(), 1); //initial commit is on the top
0302     QVERIFY(commits[1].parents().isEmpty());  //0 is later than 1!
0303     QCOMPARE(commits[0].log(), QString("commit 1"));  //0 is later than 1!
0304     QCOMPARE(commits[1].log(), QString("commit 0"));
0305     QVERIFY(commits[1].commit().contains(QRegExp("^\\w{,40}$")));
0306     QVERIFY(commits[0].commit().contains(QRegExp("^\\w{,40}$")));
0307     QVERIFY(commits[0].parents()[0].contains(QRegExp("^\\w{,40}$")));
0308 }
0309 
0310 void MercurialTest::removeTempDirs()
0311 {
0312     if (QFileInfo(mercurialTest_BaseDir).exists())
0313         if (!(KIO::del(QUrl::fromLocalFile(mercurialTest_BaseDir))->exec()))
0314             qCDebug(PLUGIN_MERCURIAL) << "KIO::del(" << mercurialTest_BaseDir << ") returned false";
0315 
0316     if (QFileInfo(mercurialTest_BaseDir2).exists())
0317         if (!(KIO::del(QUrl::fromLocalFile(mercurialTest_BaseDir2))->exec()))
0318             qCDebug(PLUGIN_MERCURIAL) << "KIO::del(" << mercurialTest_BaseDir2 << ") returned false";
0319 }
0320 
0321 void MercurialTest::testAnnotate()
0322 {
0323     repoInit();
0324     addFiles();
0325     commitFiles();
0326 
0327     // TODO: Check annotation with a lot of commits (> 200)
0328     VcsRevision revision;
0329     revision.setRevisionValue(0, KDevelop::VcsRevision::GlobalNumber);
0330     auto job = m_proxy->annotate(QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName), revision);
0331     verifyJobSucceed(job);
0332 
0333     auto results = job->fetchResults().toList();
0334     QCOMPARE(results.size(), 2);
0335 
0336     auto commit0 = results[0].value<VcsAnnotationLine>();
0337     QCOMPARE(commit0.text(), QStringLiteral("commit 0 content"));
0338     QCOMPARE(commit0.lineNumber(), 0);
0339     QVERIFY(commit0.date().isValid());
0340     QCOMPARE(commit0.revision().revisionValue().toLongLong(), 0);
0341 
0342     auto commit1 = results[1].value<VcsAnnotationLine>();
0343     QCOMPARE(commit1.text(), QStringLiteral("commit 1 content"));
0344     QCOMPARE(commit1.lineNumber(), 1);
0345     QVERIFY(commit1.date().isValid());
0346     QCOMPARE(commit1.revision().revisionValue().toLongLong(), 1);
0347 
0348     // Let's change a file without commiting it
0349     writeToFile(mercurialTest_BaseDir + mercurialTest_FileName, "commit 2 content (temporary)", QIODevice::Append);
0350 
0351     job = m_proxy->annotate(QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName), revision);
0352     verifyJobSucceed(job);
0353 
0354     results = job->fetchResults().toList();
0355     QCOMPARE(results.size(), 3);
0356     auto commit2 = results[2].value<VcsAnnotationLine>();
0357     QCOMPARE(commit2.text(), QStringLiteral("commit 2 content (temporary)"));
0358     QCOMPARE(commit2.lineNumber(), 2);
0359     QVERIFY(commit2.date().isValid());
0360     QCOMPARE(commit2.revision().revisionValue().toLongLong(), 2);
0361     QCOMPARE(commit2.author(), QStringLiteral("not.committed.yet"));
0362     QCOMPARE(commit2.commitMessage(), QStringLiteral("Not Committed Yet"));
0363 
0364     // Check that commit 2 is stripped and mercurialTest_FileName is still modified
0365     job = m_proxy->status({QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName)}, KDevelop::IBasicVersionControl::Recursive);
0366     verifyJobSucceed(job);
0367 
0368     auto statusResults = job->fetchResults().toList();
0369     QCOMPARE(statusResults.size(), 1);
0370     auto status = statusResults[0].value<VcsStatusInfo>();
0371     QCOMPARE(status.url(), QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName));
0372     QCOMPARE(status.state(), VcsStatusInfo::ItemModified);
0373 }
0374 
0375 void MercurialTest::testDiff()
0376 {
0377     repoInit();
0378     addFiles();
0379     commitFiles();
0380 
0381     writeToFile(mercurialTest_BaseDir + mercurialTest_FileName, "commit 2 content (temporary)", QIODevice::Append);
0382 
0383     VcsRevision srcrev = VcsRevision::createSpecialRevision(VcsRevision::Base);
0384     VcsRevision dstrev = VcsRevision::createSpecialRevision(VcsRevision::Working);
0385     VcsJob* j = m_proxy->diff(QUrl::fromLocalFile(mercurialTest_BaseDir), srcrev, dstrev, IBasicVersionControl::Recursive);
0386     verifyJobSucceed(j);
0387 
0388     KDevelop::VcsDiff d = j->fetchResults().value<KDevelop::VcsDiff>();
0389     QCOMPARE(d.baseDiff().toLocalFile(), mercurialTest_BaseDir.left(mercurialTest_BaseDir.size() - 1));
0390     QVERIFY(d.diff().contains(QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName).toLocalFile()));
0391 }
0392 
0393 void MercurialTest::testAnnotateFailed()
0394 {
0395     repoInit();
0396     addFiles();
0397     commitFiles();
0398 
0399     auto verifyAnnotateJobFailed = [this](MercurialAnnotateJob::TestCase test)
0400     {
0401         VcsRevision revision;
0402         revision.setRevisionValue(0, KDevelop::VcsRevision::GlobalNumber);
0403         auto job = m_proxy->annotate(QUrl::fromLocalFile(mercurialTest_BaseDir + mercurialTest_FileName), revision);
0404         auto annotateJob = dynamic_cast<MercurialAnnotateJob*>(job);
0405         QVERIFY(annotateJob);
0406         annotateJob->m_testCase = test;
0407 
0408         QVERIFY(job->exec());
0409         QVERIFY(job->status() == KDevelop::VcsJob::JobFailed);
0410     };
0411     verifyAnnotateJobFailed(MercurialAnnotateJob::TestCase::Status);
0412     //verifyAnnotateJobFailed(MercurialAnnotateJob::TestCase::Commit);
0413     verifyAnnotateJobFailed(MercurialAnnotateJob::TestCase::Annotate);
0414     verifyAnnotateJobFailed(MercurialAnnotateJob::TestCase::Log);
0415     //verifyAnnotateJobFailed(MercurialAnnotateJob::TestCase::Strip);
0416 }
0417 
0418 void MercurialTest::testHeads()
0419 {
0420     repoInit();
0421     addFiles();
0422     commitFiles();
0423 
0424     VcsRevision revision;
0425     revision.setRevisionValue(0, KDevelop::VcsRevision::GlobalNumber);
0426     auto job = m_proxy->checkoutHead(QUrl::fromLocalFile(mercurialTest_BaseDir), revision);
0427     verifyJobSucceed(job);
0428 
0429     writeToFile(mercurialTest_BaseDir + mercurialTest_FileName, "new head content", QIODevice::Append);
0430 
0431     auto j = m_proxy->commit(QString("new head"), {QUrl::fromLocalFile(mercurialTest_BaseDir)}, KDevelop::IBasicVersionControl::Recursive);
0432     verifyJobSucceed(j);
0433 
0434     QScopedPointer<MercurialHeadsModel> headsModel(new MercurialHeadsModel(m_proxy, QUrl::fromLocalFile(mercurialTest_BaseDir), nullptr));
0435     headsModel->update();
0436 
0437     QSignalSpy waiter(headsModel.data(), &MercurialHeadsModel::updateComplete);
0438     QVERIFY(waiter.wait());
0439     QVERIFY(waiter.count() == 1);
0440 
0441     QCOMPARE(headsModel->rowCount(), 2);
0442     auto rev2 = headsModel->data(headsModel->index(0, VcsBasicEventModel::RevisionColumn)).toLongLong();
0443     auto rev1 = headsModel->data(headsModel->index(1, VcsBasicEventModel::RevisionColumn)).toLongLong();
0444     QCOMPARE(rev2, 2);
0445     QCOMPARE(rev1, 1);
0446 }
0447 
0448 QTEST_MAIN(MercurialTest)