diff --git a/autotests/src/kateview_test.cpp b/autotests/src/kateview_test.cpp index 8c77ebe6..06c952e8 100644 --- a/autotests/src/kateview_test.cpp +++ b/autotests/src/kateview_test.cpp @@ -1,503 +1,511 @@ /* This file is part of the KDE libraries Copyright (C) 2010 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kateview_test.h" #include "moc_kateview_test.cpp" #include #include #include #include #include #include #include #include #include #define testNewRow() (QTest::newRow(QString("line %1").arg(__LINE__).toLatin1().data())) using namespace KTextEditor; QTEST_MAIN(KateViewTest) KateViewTest::KateViewTest() : QObject() { KTextEditor::EditorPrivate::enableUnitTestMode(); } KateViewTest::~KateViewTest() { } void KateViewTest::testCoordinatesToCursor() { KTextEditor::DocumentPrivate doc(false, false); doc.setText("Hi World!\nHi\n"); KTextEditor::View *view1 = static_cast(doc.createView(nullptr)); view1->resize(400, 300); view1->show(); QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(0, 2))), KTextEditor::Cursor(0, 2)); QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 1))), KTextEditor::Cursor(1, 1)); // behind end of line should give an invalid cursor QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 5))), KTextEditor::Cursor::invalid()); QCOMPARE(view1->cursorToCoordinate(KTextEditor::Cursor(3, 1)), QPoint(-1, -1)); // check consistency between cursorToCoordinate(view->cursorPosition() and cursorPositionCoordinates() // random position view1->setCursorPosition(KTextEditor::Cursor(0, 3)); QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(view1->cursorPosition())), KTextEditor::Cursor(0, 3)); QCOMPARE(view1->coordinatesToCursor(view1->cursorPositionCoordinates()), KTextEditor::Cursor(0, 3)); // end of line view1->setCursorPosition(KTextEditor::Cursor(0, 9)); QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(0, 9))), KTextEditor::Cursor(0, 9)); QCOMPARE(view1->coordinatesToCursor(view1->cursorPositionCoordinates()), KTextEditor::Cursor(0, 9)); // empty line view1->setCursorPosition(KTextEditor::Cursor(2, 0)); QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(2, 0))), KTextEditor::Cursor(2, 0)); QCOMPARE(view1->coordinatesToCursor(view1->cursorPositionCoordinates()), KTextEditor::Cursor(2, 0)); // same test again, but with message widget on top visible KTextEditor::Message *message = new KTextEditor::Message("Jo World!", KTextEditor::Message::Information); doc.postMessage(message); // wait 500ms until show animation is finished, so the message widget is visible QTest::qWait(500); QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(0, 2))), KTextEditor::Cursor(0, 2)); QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 1))), KTextEditor::Cursor(1, 1)); // behind end of line should give an invalid cursor QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 5))), KTextEditor::Cursor::invalid()); QCOMPARE(view1->cursorToCoordinate(KTextEditor::Cursor(3, 1)), QPoint(-1, -1)); } void KateViewTest::testCursorToCoordinates() { KTextEditor::DocumentPrivate doc(false, false); doc.setText("int a;"); KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); view->config()->setDynWordWrap(true); view->show(); // don't crash, see https://bugs.kde.org/show_bug.cgi?id=337863 view->cursorToCoordinate(Cursor(0, 0)); view->cursorToCoordinate(Cursor(1, 0)); view->cursorToCoordinate(Cursor(-1, 0)); } void KateViewTest::testReloadMultipleViews() { QTemporaryFile file("XXXXXX.cpp"); file.open(); QTextStream stream(&file); const QString line = "const char* foo = \"asdf\"\n"; for (int i = 0; i < 200; ++i) { stream << line; } +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) stream << flush; +#else + stream << Qt::flush; +#endif file.close(); KTextEditor::DocumentPrivate doc; QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); QCOMPARE(doc.highlightingMode(), QString("C++")); KTextEditor::ViewPrivate *view1 = new KTextEditor::ViewPrivate(&doc, nullptr); KTextEditor::ViewPrivate *view2 = new KTextEditor::ViewPrivate(&doc, nullptr); view1->show(); view2->show(); QCOMPARE(doc.views().count(), 2); QVERIFY(doc.documentReload()); } void KateViewTest::testTabCursorOnReload() { // testcase for https://bugs.kde.org/show_bug.cgi?id=258480 QTemporaryFile file("XXXXXX.cpp"); file.open(); QTextStream stream(&file); stream << "\tfoo\n"; file.close(); KTextEditor::DocumentPrivate doc; QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); const KTextEditor::Cursor cursor(0, 4); view->setCursorPosition(cursor); QCOMPARE(view->cursorPosition(), cursor); QVERIFY(doc.documentReload()); QCOMPARE(view->cursorPosition(), cursor); } void KateViewTest::testLowerCaseBlockSelection() { // testcase for https://bugs.kde.org/show_bug.cgi?id=258480 KTextEditor::DocumentPrivate doc; doc.setText("nY\nnYY\n"); KTextEditor::ViewPrivate *view1 = new KTextEditor::ViewPrivate(&doc, nullptr); view1->setBlockSelection(true); view1->setSelection(Range(0, 1, 1, 3)); view1->lowercase(); QCOMPARE(doc.text(), QString("ny\nnyy\n")); } namespace { QWidget *findViewInternal(KTextEditor::View *view) { for (QObject *child : view->children()) { if (child->metaObject()->className() == QByteArrayLiteral("KateViewInternal")) { return qobject_cast(child); } } return nullptr; } } void KateViewTest::testSelection() { // see also: https://bugs.kde.org/show_bug.cgi?id=277422 // wrong behavior before: // Open file with text // click at end of some line (A) and drag to right, i.e. without selecting anything // click somewhere else (B) // shift click to another place (C) // => expected: selection from B to C // => actual: selection from A to C QTemporaryFile file("XXXXXX.txt"); file.open(); QTextStream stream(&file); stream << "A\n" << "B\n" << "C"; +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) stream << flush; +#else + stream << Qt::flush; +#endif file.close(); KTextEditor::DocumentPrivate doc; QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); view->resize(100, 200); view->show(); QObject *internalView = findViewInternal(view); QVERIFY(internalView); const QPoint afterA = view->cursorToCoordinate(Cursor(0, 1)); const QPoint afterB = view->cursorToCoordinate(Cursor(1, 1)); const QPoint afterC = view->cursorToCoordinate(Cursor(2, 1)); // click after A QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterA, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterA, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); QCOMPARE(view->cursorPosition(), Cursor(0, 1)); // drag to right QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterA, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseMove, afterA + QPoint(50, 0), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterA + QPoint(50, 0), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); QCOMPARE(view->cursorPosition(), Cursor(0, 1)); QVERIFY(!view->selection()); // click after C QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterC, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterC, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); QCOMPARE(view->cursorPosition(), Cursor(2, 1)); // shift+click after B QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterB, Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier)); QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterB, Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier)); QCOMPARE(view->cursorPosition(), Cursor(1, 1)); QCOMPARE(view->selectionRange(), Range(1, 1, 2, 1)); } void KateViewTest::testDeselectByArrowKeys_data() { QTest::addColumn("text"); testNewRow() << "foobarhaz"; testNewRow() << "كلسشمن يتبكسب"; // We all win, translates Google } void KateViewTest::testDeselectByArrowKeys() { QFETCH(QString, text); KTextEditor::DocumentPrivate doc; doc.setText(text); KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); KTextEditor::Cursor cur1(0, 3); // Start of bar: foo|barhaz KTextEditor::Cursor cur2(0, 6); // End of bar: foobar|haz KTextEditor::Cursor curDelta(0, 1); Range range(cur1, cur2); // Select "bar" // RTL drives me nuts! KTextEditor::Cursor help; if (text.isRightToLeft()) { help = cur1; cur1 = cur2; cur2 = help; } view->setSelection(range); view->setCursorPositionInternal(cur1); view->cursorLeft(); QCOMPARE(view->cursorPosition(), cur1); // Be at begin: foo|barhaz QCOMPARE(view->selection(), false); view->setSelection(range); view->setCursorPositionInternal(cur1); view->cursorRight(); QCOMPARE(view->cursorPosition(), cur2); // Be at end: foobar|haz QCOMPARE(view->selection(), false); view->config()->setValue(KateViewConfig::PersistentSelection, true); view->setSelection(range); view->setCursorPositionInternal(cur1); view->cursorLeft(); // RTL drives me nuts! help = text.isRightToLeft() ? (cur1 + curDelta) : (cur1 - curDelta); QCOMPARE(view->cursorPosition(), help); // Be one left: fo|obarhaz QCOMPARE(view->selection(), true); view->setSelection(range); view->setCursorPositionInternal(cur1); view->cursorRight(); // RTL drives me nuts! help = text.isRightToLeft() ? (cur1 - curDelta) : (cur1 + curDelta); QCOMPARE(view->cursorPosition(), help); // Be one right: foob|arhaz QCOMPARE(view->selection(), true); } void KateViewTest::testKillline() { KTextEditor::DocumentPrivate doc; doc.insertLines(0, {"foo", "bar", "baz"}); KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); view->setCursorPositionInternal(KTextEditor::Cursor(1, 2)); view->killLine(); QCOMPARE(doc.text(), QLatin1String("foo\nbaz\n")); doc.clear(); QVERIFY(doc.isEmpty()); doc.insertLines(0, {"foo", "bar", "baz", "xxx"}); view->setCursorPositionInternal(KTextEditor::Cursor(1, 2)); view->shiftDown(); view->killLine(); QCOMPARE(doc.text(), QLatin1String("foo\nxxx\n")); } void KateViewTest::testScrollPastEndOfDocument() { // bug 306745 KTextEditor::DocumentPrivate doc; doc.setText( QStringLiteral("0000000000\n" "1111111111\n" "2222222222\n" "3333333333\n" "4444444444")); QCOMPARE(doc.lines(), 5); KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); view->setCursorPosition({3, 5}); view->resize(400, 300); view->show(); // enable "[x] Scroll past end of document" view->config()->setValue(KateViewConfig::ScrollPastEnd, true); QCOMPARE(view->config()->scrollPastEnd(), true); // disable dynamic word wrap view->config()->setDynWordWrap(false); QCOMPARE(view->config()->dynWordWrap(), false); view->scrollDown(); view->scrollDown(); view->scrollDown(); // at this point, only lines 3333333333 and 4444444444 are visible. view->down(); QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(4, 5)); // verify, that only lines 3333333333 and 4444444444 are still visible. QCOMPARE(view->firstDisplayedLineInternal(KTextEditor::View::RealLine), 3); } void KateViewTest::testFoldFirstLine() { QTemporaryFile file("XXXXXX.cpp"); file.open(); QTextStream stream(&file); stream << "/**\n" << " * foo\n" << " */\n" << "\n" << "int main() {}\n"; file.close(); KTextEditor::DocumentPrivate doc; QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); QCOMPARE(doc.highlightingMode(), QString("C++")); KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); view->config()->setValue(KateViewConfig::FoldFirstLine, false); view->setCursorPosition({4, 0}); // initially, nothing is folded QVERIFY(view->textFolding().isLineVisible(1)); // now change the config, and expect the header to be folded view->config()->setValue(KateViewConfig::FoldFirstLine, true); qint64 foldedRangeId = 0; QVERIFY(!view->textFolding().isLineVisible(1, &foldedRangeId)); // now unfold the range QVERIFY(view->textFolding().unfoldRange(foldedRangeId)); QVERIFY(view->textFolding().isLineVisible(1)); // and save the file, we do not expect the folding to change then doc.setModified(true); doc.saveFile(); QVERIFY(view->textFolding().isLineVisible(1)); // now reload the document, nothing should change doc.setModified(false); QVERIFY(doc.documentReload()); QVERIFY(view->textFolding().isLineVisible(1)); } // test for bug https://bugs.kde.org/374163 void KateViewTest::testDragAndDrop() { KTextEditor::DocumentPrivate doc(false, false); doc.setText( "line0\n" "line1\n" "line2\n" "\n" "line4"); KTextEditor::View *view = static_cast(doc.createView(nullptr)); view->show(); view->resize(400, 300); QWidget *internalView = findViewInternal(view); QVERIFY(internalView); // select "line1\n" view->setSelection(Range(1, 0, 2, 0)); QCOMPARE(view->selectionRange(), Range(1, 0, 2, 0)); QVERIFY(QTest::qWaitForWindowExposed(view)); QTest::qWait(0); // For whatever reason needed const QPoint startDragPos = internalView->mapFrom(view, view->cursorToCoordinate(KTextEditor::Cursor(1, 2))); const QPoint endDragPos = internalView->mapFrom(view, view->cursorToCoordinate(KTextEditor::Cursor(3, 0))); const QPoint gStartDragPos = internalView->mapToGlobal(startDragPos); const QPoint gEndDragPos = internalView->mapToGlobal(endDragPos); // now drag and drop selected text to Cursor(3, 0) QMouseEvent pressEvent(QEvent::MouseButtonPress, startDragPos, gStartDragPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QCoreApplication::sendEvent(internalView, &pressEvent); // ugly workaround: Drag & Drop has own blocking event queue. Therefore, we need a single-shot timer to // break out of the blocking event queue, see (*) QTimer::singleShot(50, [&]() { QMouseEvent moveEvent(QEvent::MouseMove, endDragPos + QPoint(5, 0), gEndDragPos + QPoint(5, 0), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QMouseEvent releaseEvent(QEvent::MouseButtonRelease, endDragPos, gEndDragPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); QCoreApplication::sendEvent(internalView, &moveEvent); QCoreApplication::sendEvent(internalView, &releaseEvent); }); // (*) this somehow blocks... QMouseEvent moveEvent1(QEvent::MouseMove, endDragPos + QPoint(10, 0), gEndDragPos + QPoint(10, 0), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QCoreApplication::sendEvent(internalView, &moveEvent1); QTest::qWait(100); // final tests of dragged text QCOMPARE(doc.text(), QString("line0\n" "line2\n" "line1\n" "\n" "line4")); QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(3, 0)); QCOMPARE(view->selectionRange(), Range(2, 0, 3, 0)); } // test for bug https://bugs.kde.org/402594 void KateViewTest::testGotoMatchingBracket() { KTextEditor::DocumentPrivate doc(false, false); doc.setText("foo(bar)baz"); // 0123456789 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); const KTextEditor::Cursor cursor1(0, 3); // Starting point on open ( const KTextEditor::Cursor cursor2(0, 8); // Insert Mode differ slightly from... const KTextEditor::Cursor cursor3(0, 7); // Overwrite Mode doc.config()->setOvr(false); // Insert Mode view->setCursorPosition(cursor1); view->toMatchingBracket(); QCOMPARE(view->cursorPosition(), cursor2); view->toMatchingBracket(); QCOMPARE(view->cursorPosition(), cursor1); // Currently has it in Insert Mode also to work when the cursor is placed inside the parentheses view->setCursorPosition(cursor1 + KTextEditor::Cursor(0, 1)); view->toMatchingBracket(); QCOMPARE(view->cursorPosition(), cursor2); view->setCursorPosition(cursor2 + KTextEditor::Cursor(0, -1)); view->toMatchingBracket(); QCOMPARE(view->cursorPosition(), cursor1); doc.config()->setOvr(true); // Overwrite Mode view->setCursorPosition(cursor1); view->toMatchingBracket(); QCOMPARE(view->cursorPosition(), cursor3); view->toMatchingBracket(); QCOMPARE(view->cursorPosition(), cursor1); } // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/autotests/src/katewildcardmatcher_test.cpp b/autotests/src/katewildcardmatcher_test.cpp index 01cce023..9a592782 100644 --- a/autotests/src/katewildcardmatcher_test.cpp +++ b/autotests/src/katewildcardmatcher_test.cpp @@ -1,60 +1,60 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Sebastian Pipping * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "mode/katewildcardmatcher.h" #include #include bool testCase(const char *candidate, const char *wildcard) { qDebug("\"%s\" / \"%s\"", candidate, wildcard); return KateWildcardMatcher::exactMatch(QString(candidate), QString(wildcard)); } int main() { Q_ASSERT(testCase("abc.txt", "*.txt")); Q_ASSERT(!testCase("abc.txt", "*.cpp")); Q_ASSERT(testCase("Makefile.am", "*Makefile*")); Q_ASSERT(testCase("control", "control")); Q_ASSERT(testCase("abcd", "a??d")); Q_ASSERT(testCase("a", "?")); Q_ASSERT(testCase("a", "*?*")); Q_ASSERT(testCase("a", "*")); Q_ASSERT(testCase("a", "**")); Q_ASSERT(testCase("a", "***")); Q_ASSERT(testCase("", "*")); Q_ASSERT(testCase("", "**")); Q_ASSERT(!testCase("", "?")); Q_ASSERT(testCase("ab", "a*")); Q_ASSERT(testCase("ab", "*b")); Q_ASSERT(testCase("ab", "a?")); Q_ASSERT(testCase("ab", "?b")); Q_ASSERT(testCase("aXXbXXbYYaYc", "a*b*c")); - qDebug() << endl << "DONE"; + qDebug() << "\nDONE"; return 0; } diff --git a/autotests/src/script_test_base.cpp b/autotests/src/script_test_base.cpp index dda9b3c5..08251348 100644 --- a/autotests/src/script_test_base.cpp +++ b/autotests/src/script_test_base.cpp @@ -1,192 +1,193 @@ /** * This file is part of the KDE project * * Copyright (C) 2001,2003 Peter Kelly (pmk@post.com) * Copyright (C) 2003,2004 Stephan Kulow (coolo@kde.org) * Copyright (C) 2004 Dirk Mueller ( mueller@kde.org ) * Copyright 2006, 2007 Leo Savernik (l.savernik@aon.at) * Copyright (C) 2010 Milian Wolff * Copyright (C) 2013 Gerald Senarclens de Grancy * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ // BEGIN Includes #include "katedocument.h" #include "kateglobal.h" #include "kateview.h" #include #include #include #include #include #include -#include "testutils.h" +#include +#include "testutils.h" #include "script_test_base.h" const QString testDataPath(QLatin1String(TEST_DATA_DIR)); QtMessageHandler ScriptTestBase::m_msgHandler = nullptr; void noDebugMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { switch (type) { case QtDebugMsg: break; default: ScriptTestBase::m_msgHandler(type, context, msg); } } void ScriptTestBase::initTestCase() { KTextEditor::EditorPrivate::enableUnitTestMode(); m_msgHandler = qInstallMessageHandler(noDebugMessageOutput); m_toplevel = new QMainWindow(); m_document = new KTextEditor::DocumentPrivate(true, false, m_toplevel); m_view = static_cast(m_document->widget()); m_env = new TestScriptEnv(m_document, m_outputWasCustomised); } void ScriptTestBase::cleanupTestCase() { qInstallMessageHandler(m_msgHandler); } void ScriptTestBase::getTestData(const QString &script) { QTest::addColumn("testcase"); // make sure the script files are valid if (!m_script_dir.isEmpty()) { QFile scriptFile(QLatin1String(JS_DATA_DIR) + m_script_dir + QLatin1Char('/') + script + QLatin1String(".js")); if (scriptFile.exists()) { QVERIFY(scriptFile.open(QFile::ReadOnly)); QJSValue result = m_env->engine()->evaluate(QString::fromLatin1(scriptFile.readAll()), scriptFile.fileName()); QVERIFY2(!result.isError(), (result.toString() + QLatin1String(" in file ") + scriptFile.fileName()).toUtf8().constData()); } } const QString testDir(testDataPath + m_section + QLatin1Char('/') + script + QLatin1Char('/')); if (!QFile::exists(testDir)) { QSKIP(qPrintable(QString(testDir + QLatin1String(" does not exist"))), SkipAll); } QDirIterator contents(testDir); while (contents.hasNext()) { QString entry = contents.next(); if (entry.endsWith(QLatin1Char('.'))) { continue; } QFileInfo info(entry); if (!info.isDir()) { continue; } QTest::newRow(info.baseName().toLocal8Bit().constData()) << info.absoluteFilePath(); } } void ScriptTestBase::runTest(const ExpectedFailures &failures) { if (!QFile::exists(testDataPath + m_section)) { QSKIP(qPrintable(QString(testDataPath + m_section + QLatin1String(" does not exist"))), SkipAll); } QFETCH(QString, testcase); m_toplevel->resize(800, 600); // restore size // load page QUrl url; url.setScheme(QLatin1String("file")); url.setPath(testcase + QLatin1String("/origin")); m_document->openUrl(url); // evaluate test-script QFile sourceFile(testcase + QLatin1String("/input.js")); if (!sourceFile.open(QFile::ReadOnly)) { QFAIL(qPrintable(QString::fromLatin1("Failed to open file: %1").arg(sourceFile.fileName()))); } QTextStream stream(&sourceFile); stream.setCodec("UTF8"); QString code = stream.readAll(); sourceFile.close(); // Execute script QJSValue result = m_env->engine()->evaluate(code, testcase + QLatin1String("/input.js"), 1); QVERIFY2(!result.isError(), result.toString().toUtf8().constData()); const QString fileExpected = testcase + QLatin1String("/expected"); const QString fileActual = testcase + QLatin1String("/actual"); url.setPath(fileActual); m_document->saveAs(url); const QByteArray actualChecksum = m_document->checksum(); const QByteArray expectedChecksum = digestForFile(fileExpected); if (actualChecksum != expectedChecksum) { // diff actual and expected QProcess diff; QStringList args(QStringList() << QLatin1String("-u") << fileExpected << fileActual); diff.start(QLatin1String("diff"), args); diff.waitForFinished(); QByteArray out = diff.readAllStandardOutput(); QByteArray err = diff.readAllStandardError(); if (!err.isEmpty()) { qWarning() << err; } if (diff.exitCode() != EXIT_SUCCESS) { - QTextStream(stdout) << out << endl; + std::cout << qPrintable(out) << std::endl; } for (const Failure &failure : failures) { QEXPECT_FAIL(failure.first, failure.second, Abort); } QCOMPARE(diff.exitCode(), EXIT_SUCCESS); } m_document->closeUrl(); } QByteArray ScriptTestBase::digestForFile(const QString &file) { QByteArray digest; QFile f(file); if (f.open(QIODevice::ReadOnly)) { // init the hash with the git header QCryptographicHash crypto(QCryptographicHash::Sha1); const QString header = QString(QLatin1String("blob %1")).arg(f.size()); crypto.addData(header.toLatin1() + '\0'); while (!f.atEnd()) { crypto.addData(f.read(256 * 1024)); } digest = crypto.result(); } return digest; } diff --git a/src/export/htmlexporter.cpp b/src/export/htmlexporter.cpp index 78c7c491..fe001f4f 100644 --- a/src/export/htmlexporter.cpp +++ b/src/export/htmlexporter.cpp @@ -1,120 +1,124 @@ /* SPDX-License-Identifier: LGPL-2.0-or-later Copyright (C) 2009 Milian Wolff Copyright (C) 2002 John Firebaugh Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "htmlexporter.h" #include #include HTMLExporter::HTMLExporter(KTextEditor::View *view, QTextStream &output, const bool encapsulate) : AbstractExporter(view, output, encapsulate) { if (m_encapsulate) { // let's write the HTML header : - m_output << "" << endl; - m_output << "" << endl; - m_output << "" << endl; - m_output << "" << endl; - m_output << "" << endl; - m_output << "" << endl; + m_output << "\n"; + m_output << "\n"; + m_output << "\n"; + m_output << "\n"; + m_output << "\n"; + m_output << "\n"; // for the title, we write the name of the file (/usr/local/emmanuel/myfile.cpp -> myfile.cpp) - m_output << "" << view->document()->documentName() << "" << endl; - m_output << "" << endl; + m_output << "" << view->document()->documentName() << "\n"; + m_output << "\n"; // tell in comment which highlighting was used! - m_output << "" << endl; + m_output << "\n"; - m_output << "" << endl; + m_output << "\n"; } if (!m_defaultAttribute) { - m_output << "
" << endl;
+        m_output << "
\n";
     } else {
         m_output << QStringLiteral("
")
                         .arg(m_defaultAttribute->fontBold() ? QStringLiteral("font-weight:bold;") : QString())
                         .arg(m_defaultAttribute->fontItalic() ? QStringLiteral("font-style:italic;") : QString())
                         .arg(QLatin1String("color:") + m_defaultAttribute->foreground().color().name() + QLatin1Char(';'))
                         .arg(QLatin1String("background-color:") + m_defaultAttribute->background().color().name() + QLatin1Char(';'))
-                 << endl;
+                 << '\n';
     }
+    m_output.flush();
 }
 
 HTMLExporter::~HTMLExporter()
 {
-    m_output << "
" << endl; + m_output << "
\n"; if (m_encapsulate) { - m_output << "" << endl; - m_output << "" << endl; + m_output << "\n"; + m_output << "\n"; } + m_output.flush(); } void HTMLExporter::openLine() { } void HTMLExporter::closeLine(const bool lastLine) { if (!lastLine) { // we are inside a
, so a \n is a new line
         m_output << "\n";
+        m_output.flush();
     }
 }
 
 void HTMLExporter::exportText(const QString &text, const KTextEditor::Attribute::Ptr &attrib)
 {
     if (!attrib || !attrib->hasAnyProperty() || attrib == m_defaultAttribute) {
         m_output << text.toHtmlEscaped();
         return;
     }
 
     if (attrib->fontBold()) {
         m_output << "";
     }
     if (attrib->fontItalic()) {
         m_output << "";
     }
 
     bool writeForeground = attrib->hasProperty(QTextCharFormat::ForegroundBrush) && (!m_defaultAttribute || attrib->foreground().color() != m_defaultAttribute->foreground().color());
     bool writeBackground = attrib->hasProperty(QTextCharFormat::BackgroundBrush) && (!m_defaultAttribute || attrib->background().color() != m_defaultAttribute->background().color());
 
     if (writeForeground || writeBackground) {
         m_output << QStringLiteral("")
                         .arg(writeForeground ? QString(QLatin1String("color:") + attrib->foreground().color().name() + QLatin1Char(';')) : QString())
                         .arg(writeBackground ? QString(QLatin1String("background:") + attrib->background().color().name() + QLatin1Char(';')) : QString());
     }
 
     m_output << text.toHtmlEscaped();
 
     if (writeBackground || writeForeground) {
         m_output << "";
     }
     if (attrib->fontItalic()) {
         m_output << "";
     }
     if (attrib->fontBold()) {
         m_output << "";
     }
+    m_output.flush();
 }