summaryrefslogtreecommitdiff
path: root/src/plugins/clearcase/clearcaseplugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/clearcase/clearcaseplugin.cpp')
-rw-r--r--src/plugins/clearcase/clearcaseplugin.cpp348
1 files changed, 336 insertions, 12 deletions
diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp
index 3afb4d2fff..bc9750c61b 100644
--- a/src/plugins/clearcase/clearcaseplugin.cpp
+++ b/src/plugins/clearcase/clearcaseplugin.cpp
@@ -48,6 +48,7 @@
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
+#include <coreplugin/infobar.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/mimedatabase.h>
#include <coreplugin/progressmanager/progressmanager.h>
@@ -186,6 +187,9 @@ ClearCasePlugin::ClearCasePlugin() :
m_submitActionTriggered(false),
m_activityMutex(new QMutex),
m_statusMap(new StatusMap)
+ #ifdef WITH_TESTS
+ ,m_fakeClearTool(false)
+ #endif
{
qRegisterMetaType<ClearCase::Internal::FileStatus::Status>("ClearCase::Internal::FileStatus::Status");
}
@@ -230,12 +234,82 @@ QString ClearCasePlugin::getDriveLetterOfPath(const QString &directory)
{
// cdUp until we get just the drive letter
QDir dir(directory);
- while (dir.cdUp())
+ while (!dir.isRoot() && dir.cdUp())
{ }
return dir.path();
}
+void ClearCasePlugin::updateStatusForFile(const QString &absFile)
+{
+ setStatus(absFile, getFileStatus(absFile), false);
+}
+
+/// Give warning if a derived object is edited
+void ClearCasePlugin::updateEditDerivedObjectWarning(const QString &fileName,
+ const FileStatus::Status status)
+{
+ if (!isDynamic())
+ return;
+
+ Core::IDocument *curDocument = Core::EditorManager::currentDocument();
+ if (!curDocument)
+ return;
+
+ Core::InfoBar *infoBar = curDocument->infoBar();
+ const Core::Id derivedObjectWarning("ClearCase.DerivedObjectWarning");
+
+ if (status == FileStatus::Derived) {
+ if (!infoBar->canInfoBeAdded(derivedObjectWarning))
+ return;
+
+ infoBar->addInfo(Core::InfoBarEntry(derivedObjectWarning,
+ tr("Editing Derived Object: %1")
+ .arg(fileName)));
+ } else {
+ infoBar->removeInfo(derivedObjectWarning);
+ }
+}
+
+FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const
+{
+ QTC_CHECK(!fileName.isEmpty());
+
+ const QDir viewRootDir = QFileInfo(fileName).dir();
+ const QString viewRoot = viewRootDir.path();
+
+ QStringList args(QLatin1String("ls"));
+ args << fileName;
+ QString buffer = runCleartoolSync(viewRoot, args);
+
+ const int atatpos = buffer.indexOf(QLatin1String("@@"));
+ if (atatpos != -1) { // probably a managed file
+ const QString absFile =
+ viewRootDir.absoluteFilePath(
+ QDir::fromNativeSeparators(buffer.left(atatpos)));
+ QTC_CHECK(QFile(absFile).exists());
+ QTC_CHECK(!absFile.isEmpty());
+
+ // "cleartool ls" of a derived object looks like this:
+ // /path/to/file/export/MyFile.h@@--11-13T19:52.266580
+ const QChar c = buffer.at(atatpos + 2);
+ const bool isDerivedObject = c != QLatin1Char('/') && c != QLatin1Char('\\');
+ if (isDerivedObject)
+ return FileStatus::Derived;
+
+ // find first whitespace. anything before that is not interesting
+ const int wspos = buffer.indexOf(QRegExp(QLatin1String("\\s")));
+ if (buffer.lastIndexOf(QLatin1String("CHECKEDOUT"), wspos) != -1)
+ return FileStatus::CheckedOut;
+ else
+ return FileStatus::CheckedIn;
+ } else {
+ QTC_CHECK(QFile(fileName).exists());
+ QTC_CHECK(!fileName.isEmpty());
+ return FileStatus::NotManaged;
+ }
+}
+
///
/// Check if the directory is managed by ClearCase.
///
@@ -483,12 +557,14 @@ bool ClearCasePlugin::initialize(const QStringList & /*arguments */, QString *er
clearcaseMenu->addSeparator(globalcontext);
m_diffActivityAction = new QAction(tr("Diff A&ctivity..."), this);
+ m_diffActivityAction->setEnabled(false);
command = ActionManager::registerAction(m_diffActivityAction, CMD_ID_DIFF_ACTIVITY, globalcontext);
connect(m_diffActivityAction, SIGNAL(triggered()), this, SLOT(diffActivity()));
clearcaseMenu->addAction(command);
m_commandLocator->appendCommand(command);
m_checkInActivityAction = new Utils::ParameterAction(tr("Ch&eck In Activity"), tr("Chec&k In Activity \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this);
+ m_checkInActivityAction->setEnabled(false);
command = ActionManager::registerAction(m_checkInActivityAction, CMD_ID_CHECKIN_ACTIVITY, globalcontext);
connect(m_checkInActivityAction, SIGNAL(triggered()), this, SLOT(startCheckInActivity()));
command->setAttribute(Command::CA_UpdateText);
@@ -671,7 +747,16 @@ QStringList ClearCasePlugin::ccGetActiveVobs() const
return res;
}
-// file must be relative to topLevel, and using '/' path separator
+void ClearCasePlugin::checkAndReIndexUnknownFile(const QString &file)
+{
+ if (isDynamic()) {
+ // reindex unknown files
+ if (m_statusMap->value(file, FileStatus(FileStatus::Unknown)).status == FileStatus::Unknown)
+ updateStatusForFile(file);
+ }
+}
+
+// file must be absolute, and using '/' path separator
FileStatus ClearCasePlugin::vcsStatus(const QString &file) const
{
return m_statusMap->value(file, FileStatus(FileStatus::Unknown));
@@ -727,7 +812,10 @@ void ClearCasePlugin::updateStatusActions()
bool hasFile = currentState().hasFile();
if (hasFile) {
QString absoluteFileName = currentState().currentFile();
- fileStatus = m_statusMap->value(absoluteFileName, FileStatus(FileStatus::Unknown));
+ checkAndReIndexUnknownFile(absoluteFileName);
+ fileStatus = vcsStatus(absoluteFileName);
+
+ updateEditDerivedObjectWarning(absoluteFileName, fileStatus.status);
if (Constants::debug)
qDebug() << Q_FUNC_INFO << absoluteFileName << ", status = "
@@ -771,6 +859,7 @@ void ClearCasePlugin::updateActions(VcsBase::VcsBasePlugin::ActionState as)
m_annotateCurrentAction->setParameter(fileName);
m_addFileAction->setParameter(fileName);
m_updateIndexAction->setEnabled(!m_settings.disableIndexer);
+
updateStatusActions();
}
@@ -791,6 +880,7 @@ void ClearCasePlugin::addCurrentFile()
// Set the FileStatus of file given in absolute path
void ClearCasePlugin::setStatus(const QString &file, FileStatus::Status status, bool update)
{
+ QTC_CHECK(!file.isEmpty());
m_statusMap->insert(file, FileStatus(status, QFileInfo(file).permissions()));
if (update && currentState().currentFile() == file)
@@ -929,7 +1019,7 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis
if ((m_settings.diffType == GraphicalDiff) && (files.count() == 1)) {
const QString file = files.first();
const QString absFilePath = workingDir + QLatin1Char('/') + file;
- if (m_statusMap->value(absFilePath).status == FileStatus::Hijacked)
+ if (vcsStatus(absFilePath).status == FileStatus::Hijacked)
diffGraphical(ccGetFileVersion(workingDir, file), file);
else
diffGraphical(file);
@@ -943,7 +1033,7 @@ void ClearCasePlugin::ccDiffWithPred(const QString &workingDir, const QStringLis
QString result;
foreach (const QString &file, files) {
const QString absFilePath = workingDir + QLatin1Char('/') + file;
- if (m_statusMap->value(QDir::fromNativeSeparators(absFilePath)).status == FileStatus::Hijacked)
+ if (vcsStatus(QDir::fromNativeSeparators(absFilePath)).status == FileStatus::Hijacked)
result += diffExternal(ccGetFileVersion(workingDir, file), file);
else
result += diffExternal(file);
@@ -1235,7 +1325,8 @@ void ClearCasePlugin::viewStatus()
m_viewData = ccGetView(m_topLevel);
QTC_ASSERT(!m_viewData.name.isEmpty() && !m_settings.disableIndexer, return);
VcsBase::VcsBaseOutputWindow *outputwindow = VcsBase::VcsBaseOutputWindow::instance();
- outputwindow->appendCommand(QLatin1String("Indexed files status (C=Checked Out, H=Hijacked, ?=Missing)"));
+ outputwindow->appendCommand(QLatin1String("Indexed files status (C=Checked Out, "
+ "H=Hijacked, ?=Missing)"));
bool anymod = false;
for (StatusMap::ConstIterator it = m_statusMap->constBegin();
it != m_statusMap->constEnd();
@@ -1481,14 +1572,14 @@ bool ClearCasePlugin::vcsOpen(const QString &workingDir, const QString &fileName
CheckOutDialog coDialog(title, m_viewData.isUcm);
if (!m_settings.disableIndexer &&
- (fi.isWritable() || m_statusMap->value(absPath).status == FileStatus::Unknown))
+ (fi.isWritable() || vcsStatus(absPath).status == FileStatus::Unknown))
QtConcurrent::run(&sync, QStringList(absPath)).waitForFinished();
- if (m_statusMap->value(absPath).status == FileStatus::CheckedOut) {
+ if (vcsStatus(absPath).status == FileStatus::CheckedOut) {
QMessageBox::information(0, tr("ClearCase Checkout"), tr("File is already checked out."));
return true;
}
// Only snapshot views can have hijacked files
- bool isHijacked = (!m_viewData.isDynamic && (m_statusMap->value(absPath).status & FileStatus::Hijacked));
+ bool isHijacked = (!m_viewData.isDynamic && (vcsStatus(absPath).status & FileStatus::Hijacked));
if (!isHijacked)
coDialog.hideHijack();
if (coDialog.exec() == QDialog::Accepted) {
@@ -1760,7 +1851,13 @@ QString ClearCasePlugin::vcsGetRepositoryURL(const QString & /*directory*/)
///
bool ClearCasePlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */) const
{
+#ifdef WITH_TESTS
+ // If running with tests and fake ClearTool is enabled, then pretend we manage every directory
+ QString topLevelFound = m_fakeClearTool ? directory : findTopLevel(directory);
+#else
QString topLevelFound = findTopLevel(directory);
+#endif
+
if (topLevel)
*topLevel = topLevelFound;
return !topLevelFound.isEmpty();
@@ -1877,9 +1974,9 @@ bool ClearCasePlugin::ccCheckUcm(const QString &viewname, const QString &working
bool ClearCasePlugin::managesFile(const QString &workingDirectory, const QString &fileName) const
{
- QStringList args;
- args << QLatin1String("ls") << fileName;
- return runCleartoolSync(workingDirectory, args).contains(QLatin1String("@@"));
+ QString absFile = QFileInfo(QDir(workingDirectory), fileName).absoluteFilePath();
+ const FileStatus::Status status = getFileStatus(absFile);
+ return status != FileStatus::NotManaged && status != FileStatus::Derived;
}
ViewData ClearCasePlugin::ccGetView(const QString &workingDir) const
@@ -2142,6 +2239,233 @@ void ClearCasePlugin::testLogResolving()
"src/plugins/clearcase/clearcaseeditor.h@@/main/branch1/branch2/9",
"src/plugins/clearcase/clearcaseeditor.h@@/main/branch1/branch2/8");
}
+
+void ClearCasePlugin::initTestCase()
+{
+ m_tempFile = QDir::currentPath() + QLatin1String("/cc_file.cpp");
+ Utils::FileSaver srcSaver(m_tempFile);
+ srcSaver.write(QByteArray());
+ srcSaver.finalize();
+}
+
+void ClearCasePlugin::cleanupTestCase()
+{
+ QVERIFY(QFile::remove(m_tempFile));
+}
+
+void ClearCasePlugin::testFileStatusParsing_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<QString>("cleartoolLsLine");
+ QTest::addColumn<int>("status");
+
+ QTest::newRow("CheckedOut")
+ << m_tempFile
+ << QString(m_tempFile + QLatin1String("@@/main/branch1/CHECKEDOUT from /main/branch1/0 Rule: CHECKEDOUT"))
+ << static_cast<int>(FileStatus::CheckedOut);
+
+ QTest::newRow("CheckedIn")
+ << m_tempFile
+ << QString(m_tempFile + QLatin1String("@@/main/9 Rule: MY_LABEL_1.6.4 [-mkbranch branch1]"))
+ << static_cast<int>(FileStatus::CheckedIn);
+
+ QTest::newRow("Hijacked")
+ << m_tempFile
+ << QString(m_tempFile + QLatin1String("@@/main/9 [hijacked] Rule: MY_LABEL_1.5.33 [-mkbranch myview1]"))
+ << static_cast<int>(FileStatus::Hijacked);
+
+
+ QTest::newRow("Missing")
+ << m_tempFile
+ << QString(m_tempFile + QLatin1String("@@/main/9 [loaded but missing] Rule: MY_LABEL_1.5.33 [-mkbranch myview1]"))
+ << static_cast<int>(FileStatus::Missing);
+}
+
+void ClearCasePlugin::testFileStatusParsing()
+{
+ ClearCasePlugin *plugin = ClearCasePlugin::instance();
+ plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap);
+
+ QFETCH(QString, filename);
+ QFETCH(QString, cleartoolLsLine);
+ QFETCH(int, status);
+
+ ClearCaseSync ccSync(plugin, plugin->m_statusMap);
+ ccSync.verifyParseStatus(filename, cleartoolLsLine, static_cast<FileStatus::Status>(status));
+}
+
+void ClearCasePlugin::testFileNotManaged()
+{
+ ClearCasePlugin *plugin = ClearCasePlugin::instance();
+ plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap);
+ ClearCaseSync ccSync(plugin, plugin->m_statusMap);
+ ccSync.verifyFileNotManaged();
+}
+
+void ClearCasePlugin::testFileCheckedOutDynamicView()
+{
+ ClearCasePlugin *plugin = ClearCasePlugin::instance();
+ plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap);
+
+ ClearCaseSync ccSync(plugin, plugin->m_statusMap);
+ ccSync.verifyFileCheckedOutDynamicView();
+}
+
+void ClearCasePlugin::testFileCheckedInDynamicView()
+{
+ ClearCasePlugin *plugin = ClearCasePlugin::instance();
+ plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap);
+ ClearCaseSync ccSync(plugin, plugin->m_statusMap);
+ ccSync.verifyFileCheckedInDynamicView();
+}
+
+void ClearCasePlugin::testFileNotManagedDynamicView()
+{
+ ClearCasePlugin *plugin = ClearCasePlugin::instance();
+ plugin->m_statusMap = QSharedPointer<StatusMap>(new StatusMap);
+ ClearCaseSync ccSync(plugin, plugin->m_statusMap);
+ ccSync.verifyFileNotManagedDynamicView();
+}
+
+namespace {
+/**
+ * @brief Convenience class which also properly cleans up editors and temp files
+ */
+class TestCase
+{
+public:
+ TestCase(const QString &fileName) :
+ m_fileName(fileName) ,
+ m_editor(0)
+ {
+ ClearCasePlugin::instance()->setFakeCleartool(true);
+ Utils::FileSaver srcSaver(fileName);
+ srcSaver.write(QByteArray());
+ srcSaver.finalize();
+
+ m_editor = Core::EditorManager::openEditor(fileName);
+
+ QCoreApplication::processEvents(); // process any pending events
+ }
+
+ ViewData dummyViewData() const
+ {
+ ViewData viewData;
+ viewData.name = QLatin1String("fake_view");
+ viewData.root = QDir::currentPath();
+ viewData.isUcm = false;
+ return viewData;
+ }
+
+ ~TestCase()
+ {
+ Core::EditorManager::closeEditor(m_editor, false);
+ QCoreApplication::processEvents(); // process any pending events
+
+ QFile file(m_fileName);
+ if (!file.isWritable()) // Windows can't delete read only files
+ file.setPermissions(file.permissions() | QFile::WriteUser);
+ QVERIFY(file.remove());
+ ClearCasePlugin::instance()->setFakeCleartool(false);
+ }
+
+private:
+ QString m_fileName;
+ Core::IEditor *m_editor;
+};
+}
+
+void ClearCasePlugin::testStatusActions_data()
+{
+ QTest::addColumn<int>("status");
+ QTest::addColumn<bool>("checkOutAction");
+ QTest::addColumn<bool>("undoCheckOutAction");
+ QTest::addColumn<bool>("undoHijackAction");
+ QTest::addColumn<bool>("checkInCurrentAction");
+ QTest::addColumn<bool>("addFileAction");
+ QTest::addColumn<bool>("checkInActivityAction");
+ QTest::addColumn<bool>("diffActivityAction");
+
+ QTest::newRow("Unknown") << static_cast<int>(FileStatus::Unknown)
+ << true << true << true << true << true << false << false;
+ QTest::newRow("CheckedOut") << static_cast<int>(FileStatus::CheckedOut)
+ << false << true << false << true << false << false << false;
+ QTest::newRow("CheckedIn") << static_cast<int>(FileStatus::CheckedIn)
+ << true << false << false << false << false << false << false;
+ QTest::newRow("NotManaged") << static_cast<int>(FileStatus::NotManaged)
+ << false << false << false << false << true << false << false;
+}
+
+void ClearCasePlugin::testStatusActions()
+{
+ const QString fileName = QDir::currentPath() + QLatin1String("/clearcase_file.cpp");
+ TestCase testCase(fileName);
+
+ m_viewData = testCase.dummyViewData();
+
+ QFETCH(int, status);
+ FileStatus::Status tempStatus = static_cast<FileStatus::Status>(status);
+
+ // special case: file should appear as "Unknown" since there is no entry in the index
+ // and we don't want to explicitly set the status for this test case
+ if (tempStatus != FileStatus::Unknown)
+ setStatus(fileName, tempStatus, true);
+
+ QFETCH(bool, checkOutAction);
+ QFETCH(bool, undoCheckOutAction);
+ QFETCH(bool, undoHijackAction);
+ QFETCH(bool, checkInCurrentAction);
+ QFETCH(bool, addFileAction);
+ QFETCH(bool, checkInActivityAction);
+ QFETCH(bool, diffActivityAction);
+
+ QCOMPARE(m_checkOutAction->isEnabled(), checkOutAction);
+ QCOMPARE(m_undoCheckOutAction->isEnabled(), undoCheckOutAction);
+ QCOMPARE(m_undoHijackAction->isEnabled(), undoHijackAction);
+ QCOMPARE(m_checkInCurrentAction->isEnabled(), checkInCurrentAction);
+ QCOMPARE(m_addFileAction->isEnabled(), addFileAction);
+ QCOMPARE(m_checkInActivityAction->isEnabled(), checkInActivityAction);
+ QCOMPARE(m_diffActivityAction->isEnabled(), diffActivityAction);
+}
+
+void ClearCasePlugin::testVcsStatusDynamicReadonlyNotManaged()
+{
+ // File is not in map, and is read-only
+ ClearCasePlugin::instance();
+ m_statusMap = QSharedPointer<StatusMap>(new StatusMap);
+
+ const QString fileName = QDir::currentPath() + QLatin1String("/readonly_notmanaged_file.cpp");
+
+ m_viewData.isDynamic = true;
+ TestCase testCase(fileName);
+
+ QFile::setPermissions(fileName, QFile::ReadOwner |
+ QFile::ReadUser |
+ QFile::ReadGroup |
+ QFile::ReadOther);
+
+ m_viewData = testCase.dummyViewData();
+ m_viewData.isDynamic = true;
+
+ QCOMPARE(vcsStatus(fileName).status, FileStatus::NotManaged);
+
+}
+
+void ClearCasePlugin::testVcsStatusDynamicNotManaged()
+{
+ ClearCasePlugin::instance();
+ m_statusMap = QSharedPointer<StatusMap>(new StatusMap);
+
+ const QString fileName = QDir::currentPath() + QLatin1String("/notmanaged_file.cpp");
+
+ m_viewData.isDynamic = true;
+ TestCase testCase(fileName);
+
+ m_viewData = testCase.dummyViewData();
+ m_viewData.isDynamic = true;
+
+ QCOMPARE(vcsStatus(fileName).status, FileStatus::NotManaged);
+}
#endif
} // namespace Internal