void PartManualTest::testShortcutOverride() { // FIXME: This test asks the user to press shortcut key sequences manually because // the result is different than when sending the key press via QTest::keyClick() // // When the key presses are sent manually, Konsole::TerminalDisplay::event() is called // and the overrideShortcut() signal is emitted by the part. // When the key presses are sent automatically, the shortcut is triggered but // Konsole::TerminalDisplay::event() is not called and the overrideShortcut() signal is // not emitted by the part. // Create a main window with a menu and a test // action with a shortcut set to Ctrl+S, which is also used by the terminal KMainWindow* mainWindow = new KMainWindow(); QMenu* fileMenu = mainWindow->menuBar()->addMenu("File"); QAction* testAction = fileMenu->addAction("Test"); testAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); connect(testAction, SIGNAL(triggered()), this, SLOT(shortcutTriggered())); // Create terminal part and embed in into the main window KParts::Part* terminalPart = createPart(); QVERIFY(terminalPart); mainWindow->setCentralWidget(terminalPart->widget()); TerminalInterface* terminal = qobject_cast<TerminalInterface*>(terminalPart); QVERIFY(terminal); terminal->sendInput("Press Ctrl+S twice.\n"); mainWindow->show(); // Test shortcut with override disabled, so the shortcut will be triggered _shortcutTriggered = false; _override = false; _overrideCalled = false; QVERIFY(connect(terminalPart, SIGNAL(overrideShortcut(QKeyEvent*,bool&)), this, SLOT(overrideShortcut(QKeyEvent*,bool&)))); //QTest::keyClick(terminalPart->widget(),Qt::Key_S,Qt::ControlModifier); _shortcutEventLoop = new QEventLoop(); _shortcutEventLoop->exec(); QVERIFY(_overrideCalled); QVERIFY(_shortcutTriggered); QVERIFY(!_override); // Test shortcut with override enabled, so the shortcut will not be triggered _override = true; _overrideCalled = false; _shortcutTriggered = false; //QTest::keyClick(terminalPart->widget(),Qt::Key_S,Qt::ControlModifier); _shortcutEventLoop->exec(); QVERIFY(_overrideCalled); QVERIFY(!_shortcutTriggered); QVERIFY(_override); delete _shortcutEventLoop; delete terminalPart; delete mainWindow; }
QWidget* createTerminalWidget(QWidget* parent = 0) { KPluginFactory* factory = KPluginLoader("libkonsolepart").factory(); KParts::ReadOnlyPart* part = factory ? (factory->create<KParts::ReadOnlyPart>(parent)) : 0; if (!part) { printf("Failed to initialize part\n"); return 0; } TerminalInterface* terminal = qobject_cast<TerminalInterface*>(part); if (!terminal) { printf("Failed to initialize terminal\n"); return 0; } terminal->showShellInDir(KUrl().path()); terminal->sendInput("cd " + KShell::quoteArg(KUrl().path()) + '\n'); terminal->sendInput("clear\n"); return part->widget(); }
void TerminalInterfaceTest::testTerminalInterface() { QString currentDirectory; QString retVal; bool result; // create a Konsole part and attempt to connect to it _terminalPart = createPart(); if (!_terminalPart) QSKIP("konsolepart not found.", SkipSingle); TerminalInterface* terminal = qobject_cast<TerminalInterface*>(_terminalPart); QVERIFY(terminal); terminal->showShellInDir(QDir::home().path()); int foregroundProcessId = terminal->foregroundProcessId(); QCOMPARE(foregroundProcessId, -1); QString foregroundProcessName = terminal->foregroundProcessName(); QCOMPARE(foregroundProcessName, QString("")); // terminalProcessId() is the user's default shell // FIXME: find a way to verify this // int terminalProcessId = terminal->terminalProcessId(); // Sleep is used to allow enough time for these to work // In Qt5 we can use QSignalSpy::wait() // Let's try using QSignalSpy // http://techbase.kde.org/Development/Tutorials/Unittests // QSignalSpy is really a QList of QLists, so we take the first // list, which corresponds to the arguments for the first signal // we caught. QSignalSpy stateSpy(_terminalPart, SIGNAL(currentDirectoryChanged(QString))); QVERIFY(stateSpy.isValid()); // Now we check to make sure we don't have any signals already QCOMPARE(stateSpy.count(), 0); // Let's trigger some signals // #1A - Test signal currentDirectoryChanged(QString) currentDirectory = QString("/tmp"); terminal->sendInput("cd " + currentDirectory + '\n'); sleep(2000); QCOMPARE(stateSpy.count(), 1); // Correct result? QList<QVariant> firstSignalArgs = stateSpy.takeFirst(); QString firstSignalState = firstSignalArgs.at(0).toString(); QCOMPARE(firstSignalState, currentDirectory); // Test KonsolePart API currentWorkingDirectory() result = QMetaObject::invokeMethod(_terminalPart, "currentWorkingDirectory", Qt::DirectConnection, Q_RETURN_ARG(QString, retVal)); QVERIFY(result); QCOMPARE(retVal, currentDirectory); // #1B - Test signal currentDirectoryChanged(QString) // Invalid directory - no signal should be emitted terminal->sendInput("cd /usrADADFASDF\n"); sleep(2000); QCOMPARE(stateSpy.count(), 0); // Should be no change since the above cd didn't work result = QMetaObject::invokeMethod(_terminalPart, "currentWorkingDirectory", Qt::DirectConnection, Q_RETURN_ARG(QString, retVal)); QVERIFY(result); QCOMPARE(retVal, currentDirectory); // Test starting a new program QString command = "top"; terminal->sendInput(command + '\n'); sleep(2000); // FIXME: find a good way to validate process id of 'top' foregroundProcessId = terminal->foregroundProcessId(); QVERIFY(foregroundProcessId != -1); foregroundProcessName = terminal->foregroundProcessName(); QCOMPARE(foregroundProcessName, command); terminal->sendInput("q"); sleep(2000); // Nothing running in foreground foregroundProcessId = terminal->foregroundProcessId(); QCOMPARE(foregroundProcessId, -1); foregroundProcessName = terminal->foregroundProcessName(); QCOMPARE(foregroundProcessName, QString("")); // Test destroyed() QSignalSpy destroyedSpy(_terminalPart, SIGNAL(destroyed())); QVERIFY(destroyedSpy.isValid()); // Now we check to make sure we don't have any signals already QCOMPARE(destroyedSpy.count(), 0); delete _terminalPart; QCOMPARE(destroyedSpy.count(), 1); }
void InteractiveTerminalPage::onActivate() { if ( m_termHostWidget ) return; // For whatever reason, instead of simply linking against a library we // need to do a runtime query to KService just to get a sodding terminal // widget. KService::Ptr service = KService::serviceByDesktopName( "konsolepart" ); if ( !service ) { // And all of this hoping the Konsole application is installed. If not, // tough cookies. // Maybe linking against a library seemed too simple and elegant so // someone decided to have a terminal widget depend on over 9000 other // KDElibs things that have nothing to do with a terminal widget, and // have the loading happen at runtime so it's more likely to fail at // an inconvenient time. QMessageBox::critical( this, tr( "Konsole not installed"), tr( "Please install the kde konsole and try again!" ), QMessageBox::Ok); return ; } // Create one instance of konsolepart. KParts::ReadOnlyPart* p = service->createInstance< KParts::ReadOnlyPart >( this, this, {} ); if ( !p ) { // One more opportunity for the loading operation to fail. QMessageBox::critical( this, tr( "Konsole not installed"), tr( "Please install the kde konsole and try again!" ), QMessageBox::Ok); return; } // Cast the konsolepart to the TerminalInterface... TerminalInterface* t = qobject_cast< TerminalInterface* >( p ); if ( !t ) { // This is why we can't have nice things. QMessageBox::critical( this, tr( "Konsole not installed"), tr( "Please install the kde konsole and try again!" ), QMessageBox::Ok); return; } // Make the widget persist even if the KPart goes out of scope... p->setAutoDeleteWidget( false ); // ... but kill the KPart if the widget goes out of scope. p->setAutoDeletePart( true ); m_termHostWidget = p->widget(); m_layout->addWidget( m_termHostWidget ); cDebug() << "Part widget ought to be" << m_termHostWidget->metaObject()->className(); t->showShellInDir( QDir::home().path() ); t->sendInput( QString( "%1\n" ).arg( m_command ) ); }