// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0


#include <QTest>
#include <QTemporaryFile>
#include <QSignalSpy>
#include <QStandardPaths>

#include <qcoreapplication.h>
#include <qdebug.h>
#include <qfiledialog.h>
#include <qabstractitemdelegate.h>
#include <qitemdelegate.h>
#include <qlistview.h>
#include <qcombobox.h>
#include <qpushbutton.h>
#include <qtoolbutton.h>
#include <qtreeview.h>
#include <qheaderview.h>
#include <qcompleter.h>
#include <qaction.h>
#include <qdialogbuttonbox.h>
#include <qsortfilterproxymodel.h>
#include <qlineedit.h>
#include <qlayout.h>
#include <qmenu.h>
#include <qrandom.h>
#include "../../../../../src/widgets/dialogs/qsidebar_p.h"
#include "../../../../../src/gui/itemmodels/qfilesystemmodel_p.h"
#include "../../../../../src/widgets/dialogs/qfiledialog_p.h"

#include <private/qguiapplication_p.h>

#include <qpa/qplatformdialoghelper.h>
#include <qpa/qplatformintegration.h>

#include "../../../../shared/filesystem.h"

#include <QtWidgets/private/qapplication_p.h>

#if defined QT_BUILD_INTERNAL
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT bool qt_test_isFetchedRoot();
Q_GUI_EXPORT void qt_test_resetFetchedRoot();
QT_END_NAMESPACE
#endif

static QByteArray msgDoesNotExist(const QString &name)
{
    return (QLatin1Char('"') + QDir::toNativeSeparators(name)
        + QLatin1String("\" does not exist.")).toLocal8Bit();
}

class tst_QFileDialog2 : public QObject
{
Q_OBJECT

public:
    tst_QFileDialog2();

private slots:
    void initTestCase();
    void init();
    void cleanup();

#ifdef QT_BUILD_INTERNAL
    void deleteDirAndFiles();
    void listRoot();
    void task227304_proxyOnFileDialog();
    void task236402_dontWatchDeletedDir();
    void task251321_sideBarHiddenEntries();
    void task251341_sideBarRemoveEntries();
    void task257579_sideBarWithNonCleanUrls();
#endif
    void heapCorruption();
    void filter();
    void showNameFilterDetails();
    void unc();
    void emptyUncPath();

#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_MENU)
    void task143519_deleteAndRenameActionBehavior();
#endif
    void task178897_minimumSize();
    void task180459_lastDirectory_data();
    void task180459_lastDirectory();
#ifndef Q_OS_MAC
    void task227930_correctNavigationKeyboardBehavior();
#endif
#if defined(Q_OS_WIN)
    void task226366_lowerCaseHardDriveWindows();
#endif
    void completionOnLevelAfterRoot();
    void task233037_selectingDirectory();
    void task235069_hideOnEscape_data();
    void task235069_hideOnEscape();
    void task203703_returnProperSeparator();
    void task228844_ensurePreviousSorting();
    void task239706_editableFilterCombo();
    void task218353_relativePaths();
    void task254490_selectFileMultipleTimes();
    void task259105_filtersCornerCases();

    void QTBUG4419_lineEditSelectAll();
    void QTBUG6558_showDirsOnly();
    void QTBUG4842_selectFilterWithHideNameFilterDetails();
    void dontShowCompleterOnRoot();
    void nameFilterParsing_data();
    void nameFilterParsing();
#if QT_CONFIG(settings)
    void settingsCompatibility_data();
    void settingsCompatibility();
#endif

private:
    void cleanupSettingsFile();

    QTemporaryDir tempDir;
};

tst_QFileDialog2::tst_QFileDialog2()
    : tempDir(QDir::tempPath() + "/tst_qfiledialog2.XXXXXX")
{
    QCoreApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
}

void tst_QFileDialog2::cleanupSettingsFile()
{
    // clean up the sidebar between each test
    QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
    settings.beginGroup(QLatin1String("FileDialog"));
    settings.remove(QString());
    settings.endGroup();
    settings.beginGroup(QLatin1String("Qt")); // Compatibility settings
    settings.remove(QLatin1String("filedialog"));
    settings.endGroup();
}

void tst_QFileDialog2::initTestCase()
{
    QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
    QStandardPaths::setTestModeEnabled(true);
    cleanupSettingsFile();
}

void tst_QFileDialog2::init()
{
    QFileDialogPrivate::setLastVisitedDirectory(QUrl());
    // populate the sidebar with some default settings
    QFileDialog fd;
}

void tst_QFileDialog2::cleanup()
{
    cleanupSettingsFile();
}

#ifdef QT_BUILD_INTERNAL
void tst_QFileDialog2::listRoot()
{
    QFileInfoGatherer fileInfoGatherer;
    fileInfoGatherer.start();
    QTest::qWait(1500);
    qt_test_resetFetchedRoot();
    QString dir(QDir::currentPath());
    QFileDialog fd(0, QString(), dir);
    fd.show();
    QCOMPARE(qt_test_isFetchedRoot(),false);
    fd.setDirectory("");
    QTRY_COMPARE(qt_test_isFetchedRoot(),true);
}
#endif

void tst_QFileDialog2::heapCorruption()
{
    QList<QFileDialog *> dialogs;
    for (int i=0; i < 10; i++) {
        QFileDialog *f = new QFileDialog(NULL);
        dialogs << f;
    }
    qDeleteAll(dialogs);
}

struct FriendlyQFileDialog : public QFileDialog
{
    friend class tst_QFileDialog2;
    Q_DECLARE_PRIVATE(QFileDialog)
};


#ifdef QT_BUILD_INTERNAL
void tst_QFileDialog2::deleteDirAndFiles()
{
    QString tempPath = tempDir.path() + "/QFileDialogTestDir4FullDelete";
    QDir dir;
    QVERIFY(dir.mkpath(tempPath + "/foo"));
    QVERIFY(dir.mkpath(tempPath + "/foo/B"));
    QVERIFY(dir.mkpath(tempPath + "/foo/B"));
    QVERIFY(dir.mkpath(tempPath + "/foo/c"));
    QVERIFY(dir.mkpath(tempPath + "/bar"));
    QFile(tempPath + "/foo/a");
    QTemporaryFile *t;
    t = new QTemporaryFile(tempPath + "/foo/aXXXXXX");
    t->setAutoRemove(false);
    QVERIFY2(t->open(), qPrintable(t->errorString()));
    t->close();
    delete t;

    t = new QTemporaryFile(tempPath + "/foo/B/yXXXXXX");
    t->setAutoRemove(false);
    QVERIFY2(t->open(), qPrintable(t->errorString()));
    t->close();
    delete t;
    FriendlyQFileDialog fd;
    fd.d_func()->removeDirectory(tempPath);
    QTRY_VERIFY(!QFileInfo::exists(tempPath));
}
#endif

void tst_QFileDialog2::filter()
{
    QFileDialog fd;
    QAction *hiddenAction = fd.findChild<QAction*>("qt_show_hidden_action");
    QVERIFY(hiddenAction);
    QVERIFY(hiddenAction->isEnabled());
    QVERIFY(!hiddenAction->isChecked());
    QDir::Filters filter = fd.filter();
    filter |= QDir::Hidden;
    fd.setFilter(filter);
    QVERIFY(hiddenAction->isChecked());
}

void tst_QFileDialog2::showNameFilterDetails()
{
    QFileDialog fd;
    QComboBox *filters = fd.findChild<QComboBox*>("fileTypeCombo");
    QVERIFY(filters);
    QVERIFY(!fd.testOption(QFileDialog::HideNameFilterDetails));


    QStringList filterChoices;
    filterChoices << "Image files (*.png *.xpm *.jpg)"
                  << "Text files (*.txt)"
                  << "Any files (*.*)";
    fd.setNameFilters(filterChoices);

    fd.setOption(QFileDialog::HideNameFilterDetails, true);
    QCOMPARE(filters->itemText(0), QString("Image files"));
    QCOMPARE(filters->itemText(1), QString("Text files"));
    QCOMPARE(filters->itemText(2), QString("Any files"));

    fd.setOption(QFileDialog::HideNameFilterDetails, false);
    QCOMPARE(filters->itemText(0), filterChoices.at(0));
    QCOMPARE(filters->itemText(1), filterChoices.at(1));
    QCOMPARE(filters->itemText(2), filterChoices.at(2));
}

void tst_QFileDialog2::unc()
{
#if defined(Q_OS_WIN)
    // Only test UNC on Windows./
    QString dir("\\\\"  + QTest::uncServerName() + "\\testsharewritable");
#else
    QString dir(QDir::currentPath());
#endif
    QVERIFY2(QFile::exists(dir), msgDoesNotExist(dir).constData());
    QFileDialog fd(0, QString(), dir);
    QFileSystemModel *model = fd.findChild<QFileSystemModel*>("qt_filesystem_model");
    QVERIFY(model);
    QCOMPARE(model->index(fd.directory().absolutePath()), model->index(dir));
}

void tst_QFileDialog2::emptyUncPath()
{
    QFileDialog fd;
    fd.show();
    QLineEdit *lineEdit = fd.findChild<QLineEdit*>("fileNameEdit");
    QVERIFY(lineEdit);
    // press 'keys' for the input
    for (int i = 0; i < 3 ; ++i)
        QTest::keyPress(lineEdit, Qt::Key_Backslash);
    QFileSystemModel *model = fd.findChild<QFileSystemModel*>("qt_filesystem_model");
    QVERIFY(model);
}

#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_MENU)
struct MenuCloser : public QObject {
    QWidget *w;
    explicit MenuCloser(QWidget *w) : w(w) {}

    void close()
    {
        QMenu *menu = w->findChild<QMenu*>();
        if (!menu) {
            qDebug("%s: cannot find file dialog child of type QMenu", Q_FUNC_INFO);
            w->close();
        }
        QTest::keyClick(menu, Qt::Key_Escape);
    }
};
static bool openContextMenu(QFileDialog &fd)
{
    QListView *list = fd.findChild<QListView*>("listView");
    if (!list) {
        qDebug("%s: didn't find file dialog child \"listView\"", Q_FUNC_INFO);
        return false;
    }
    QTimer timer;
    timer.setInterval(300);
    timer.setSingleShot(true);
    MenuCloser closer(&fd);
    QObject::connect(&timer, &QTimer::timeout, &closer, &MenuCloser::close);
    timer.start();
    QContextMenuEvent cme(QContextMenuEvent::Mouse, QPoint(10, 10), list->viewport()->mapToGlobal(QPoint(10, 10)));
    qApp->sendEvent(list->viewport(), &cme); // blocks until menu is closed again.
    return true;
}

void tst_QFileDialog2::task143519_deleteAndRenameActionBehavior()
{
    // test based on task233037_selectingDirectory

    struct TestContext {
        explicit TestContext(const QString &path) : current(path) {}
        ~TestContext() {
            file.remove();
            current.rmdir(test.dirName());
        }
        QDir current;
        QDir test;
        QFile file;
    };

    TestContext ctx(tempDir.path());

    // setup testbed
    QVERIFY(ctx.current.mkdir("task143519_deleteAndRenameActionBehavior_test")); // ensure at least one item
    ctx.test = ctx.current;
    QVERIFY(ctx.test.cd("task143519_deleteAndRenameActionBehavior_test"));
    ctx.file.setFileName(ctx.test.absoluteFilePath("hello"));
    QVERIFY(ctx.file.open(QIODevice::WriteOnly));
    QVERIFY(ctx.file.permissions() & QFile::WriteUser);
    ctx.file.close();

    QFileDialog fd;
    fd.setViewMode(QFileDialog::List);
    fd.setDirectory(ctx.test.absolutePath());
    fd.selectFile(ctx.file.fileName());
    fd.show();

    QVERIFY(QTest::qWaitForWindowExposed(&fd));

    // grab some internals:
    QAction *rm = fd.findChild<QAction*>("qt_delete_action");
    QVERIFY(rm);
    QAction *mv = fd.findChild<QAction*>("qt_rename_action");
    QVERIFY(mv);

    // these are the real test cases:

    // defaults
    QVERIFY(openContextMenu(fd));
    QCOMPARE(fd.selectedFiles(), QStringList(ctx.file.fileName()));
    QCOMPARE(rm->isEnabled(), !fd.testOption(QFileDialog::ReadOnly));
    QCOMPARE(mv->isEnabled(), !fd.testOption(QFileDialog::ReadOnly));

    // change to non-defaults:
    fd.setOption(QFileDialog::ReadOnly, !fd.testOption(QFileDialog::ReadOnly));

    QVERIFY(openContextMenu(fd));
    QCOMPARE(fd.selectedFiles().size(), 1);
    QCOMPARE(rm->isEnabled(), !fd.testOption(QFileDialog::ReadOnly));
    QCOMPARE(mv->isEnabled(), !fd.testOption(QFileDialog::ReadOnly));

    // and changed back to defaults:
    fd.setOption(QFileDialog::ReadOnly, !fd.testOption(QFileDialog::ReadOnly));

    QVERIFY(openContextMenu(fd));
    QCOMPARE(fd.selectedFiles().size(), 1);
    QCOMPARE(rm->isEnabled(), !fd.testOption(QFileDialog::ReadOnly));
    QCOMPARE(mv->isEnabled(), !fd.testOption(QFileDialog::ReadOnly));
}
#endif // !QT_NO_CONTEXTMENU && !QT_NO_MENU

void tst_QFileDialog2::task178897_minimumSize()
{
    QFileDialog fd;
    QSize oldMs = fd.layout()->minimumSize();
    QStringList history = fd.history();
    history << QDir::toNativeSeparators("/verylongdirectory/"
            "aaaaaaaaaabbbbbbbbcccccccccccddddddddddddddeeeeeeeeeeeeffffffffffgggtggggggggghhhhhhhhiiiiiijjjk");
    fd.setHistory(history);
    fd.show();

    QSize ms = fd.layout()->minimumSize();
    QVERIFY(ms.width() <= oldMs.width());
}

void tst_QFileDialog2::task180459_lastDirectory_data()
{
    QTest::addColumn<QString>("path");
    QTest::addColumn<QString>("directory");
    QTest::addColumn<bool>("isEnabled");
    QTest::addColumn<QString>("result");

    QTest::newRow("path+file") << QDir::homePath() + QDir::separator() + "Vugiu1co"
            << QDir::homePath()  << true
            << QDir::homePath() + QDir::separator() + "Vugiu1co"  ;
    QTest::newRow("no path") << ""
            << tempDir.path() << false << QString();
    QTest::newRow("file") << "foo"
            << QDir::currentPath() << true
            << QDir::currentPath() + QDir::separator() + "foo"  ;
    QTest::newRow("path") << QDir::homePath()
            << QDir::homePath() << false << QString();
    QTest::newRow("path not existing") << "/usr/bin/foo/bar/foo/foo.txt"
            << tempDir.path() << true
            << tempDir.path() + QDir::separator() + "foo.txt";

}

void tst_QFileDialog2::task180459_lastDirectory()
{
    if (!QGuiApplication::platformName().compare(QLatin1String("cocoa"), Qt::CaseInsensitive))
        QSKIP("Insignificant on OSX"); //QTBUG-39183
    //first visit the temp directory and close the dialog
    QFileDialog *dlg = new QFileDialog(0, "", tempDir.path());
    QFileSystemModel *model = dlg->findChild<QFileSystemModel*>("qt_filesystem_model");
    QVERIFY(model);
    QCOMPARE(model->index(tempDir.path()), model->index(dlg->directory().absolutePath()));
    delete dlg;

    QFETCH(QString, path);
    QFETCH(QString, directory);
    QFETCH(bool, isEnabled);
    QFETCH(QString, result);

    dlg = new QFileDialog(0, "", path);
    model = dlg->findChild<QFileSystemModel*>("qt_filesystem_model");
    QVERIFY(model);
    dlg->setAcceptMode(QFileDialog::AcceptSave);
    QCOMPARE(model->index(dlg->directory().absolutePath()), model->index(directory));

    QDialogButtonBox *buttonBox = dlg->findChild<QDialogButtonBox*>("buttonBox");
    QPushButton *button = buttonBox->button(QDialogButtonBox::Save);
    QVERIFY(button);
    QCOMPARE(button->isEnabled(), isEnabled);
    if (isEnabled)
        QCOMPARE(model->index(result), model->index(dlg->selectedFiles().first()));

    delete dlg;
}

#if QT_CONFIG(settings)
void tst_QFileDialog2::settingsCompatibility_data()
{
    QTest::addColumn<QString>("qtVersion");
    QTest::addColumn<QDataStream::Version>("dsVersion");
    QTest::newRow("6.2.3") << "6.2.3" << QDataStream::Qt_6_0;
    QTest::newRow("6.5") << "6.5" << QDataStream::Qt_5_0;
    QTest::newRow("15.5.2") << "5.15.2" << QDataStream::Qt_5_15;
    QTest::newRow("15.5.9") << "5.15.9" << QDataStream::Qt_5_15;
}

void tst_QFileDialog2::settingsCompatibility()
{
    static const QByteArray ba32 = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xF7\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\xFF\xFF\xFF\xFF\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""B\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00");
    static const QByteArray ba64 = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xF7\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\xFF\xFF\xFF\xFF\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""B\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00");
    QFETCH(QString, qtVersion);
    QFETCH(QDataStream::Version, dsVersion);
    // Create a header view, convert template to target format and store it in settings
    {
        QSettings settings(QSettings::UserScope, "QtProject");
        settings.beginGroup("FileDialog");
        settings.setValue("sidebarWidth", 93); // random value
        settings.setValue("shortcuts", QStringList({settings.fileName(), "/tmp"}));
        settings.setValue("qtVersion", qtVersion);
        settings.setValue("treeViewHeader", dsVersion < QDataStream::Qt_6_0 ? ba32 : ba64);
        settings.endGroup();
    }
    // Create a file dialog, read settings write them back
    {
        QFileDialog fd;
    }
    // Read back settings and compare byte array
    QSettings settings(QSettings::UserScope, "QtProject");
    settings.beginGroup("FileDialog");
    const QByteArray savedState = settings.value("treeViewHeader").toByteArray();
    QCOMPARE(savedState, ba32);
}
#endif


class FilterDirModel : public QSortFilterProxyModel
{

public:
      FilterDirModel(QString root, QObject* parent=0):QSortFilterProxyModel(parent), m_root(root)
      {}
      ~FilterDirModel()
      {};

protected:
      bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override
      {
            QModelIndex parentIndex;
            parentIndex = source_parent;

            QString path;
            path = sourceModel()->index(source_row, 0, parentIndex).data(Qt::DisplayRole).toString();

            do {
              path = parentIndex.data(Qt::DisplayRole).toString() + QLatin1Char('/') + path;
              parentIndex = parentIndex.parent();
            } while(parentIndex.isValid());

            QFileInfo info(path);
            if (info.isDir() && (QDir(path) != m_root))
                return false;
            return true;
      }


private:
      QDir m_root;


};

class sortProxy : public QSortFilterProxyModel
{
public:
        sortProxy(QObject *parent) : QSortFilterProxyModel(parent)
        {
        }
protected:
        virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
        {
            QFileSystemModel * const model = qobject_cast<QFileSystemModel *>(sourceModel());
            const QFileInfo leftInfo(model->fileInfo(left));
            const QFileInfo rightInfo(model->fileInfo(right));

            if (leftInfo.isDir() == rightInfo.isDir())
                return(leftInfo.filePath().compare(rightInfo.filePath(),Qt::CaseInsensitive) < 0);
            else if (leftInfo.isDir())
                return(false);
            else
                return(true);
        }
};

class CrashDialog : public QFileDialog
{
        Q_OBJECT

public:
        CrashDialog(QWidget *parent, const QString &caption, const
QString &dir, const QString &filter)
                   : QFileDialog(parent, caption, dir, filter)
        {
                sortProxy *proxyModel = new sortProxy(this);
                setProxyModel(proxyModel);
        }
};

#ifdef QT_BUILD_INTERNAL
void tst_QFileDialog2::task227304_proxyOnFileDialog()
{
    QFileDialog fd(0, "", QDir::currentPath(), 0);
    fd.setProxyModel(new FilterDirModel(QDir::currentPath()));
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));
    QLineEdit *edit = fd.findChild<QLineEdit*>("fileNameEdit");
    QVERIFY(edit);
    QTest::keyClick(edit, Qt::Key_T);
    QTest::keyClick(edit, Qt::Key_S);
    QTest::keyClick(edit->completer()->popup(), Qt::Key_Down);

    CrashDialog *dialog = new CrashDialog(0, QString("crash dialog test"), QDir::homePath(), QString("*") );
    dialog->setFileMode(QFileDialog::ExistingFile);
    dialog->show();
    QVERIFY(QTest::qWaitForWindowExposed(dialog));

    QListView *list = dialog->findChild<QListView*>("listView");
    QVERIFY(list);
    QTest::keyClick(list, Qt::Key_Down);
    QTest::keyClick(list, Qt::Key_Return);

    dialog->close();
    fd.close();

    QFileDialog fd2(0, "I should not crash with a proxy", tempDir.path(), {});
    QSortFilterProxyModel *pm = new QSortFilterProxyModel;
    fd2.setProxyModel(pm);
    fd2.show();
    QSidebar *sidebar = fd2.findChild<QSidebar*>("sidebar");
    sidebar->setFocus();
    sidebar->selectUrl(QUrl::fromLocalFile(QDir::homePath()));
    QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, {},
                      sidebar->visualRect(sidebar->model()->index(1, 0)).center());
    QTest::qWait(250);
    //We shouldn't crash
}
#endif

#ifndef Q_OS_MAC
// The following test implies the folder created will appear first in
// the list. On Mac files sorting depends on the locale and the order
// displayed cannot be known for sure.
void tst_QFileDialog2::task227930_correctNavigationKeyboardBehavior()
{
    QDir current = QDir::currentPath();
    current.mkdir("test");
    current.cd("test");
    QFile file("test/out.txt");
    QFile file2("test/out2.txt");
    QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
    QVERIFY(file2.open(QIODevice::WriteOnly | QIODevice::Text));
    current.cdUp();
    current.mkdir("test2");
    QFileDialog fd;
    fd.setViewMode(QFileDialog::List);
    fd.setDirectory(current.absolutePath());
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));

    // Ensure LayoutRequest event is processed so that the list view
    // is sorted correctly to have the directory entires at the top.
    QCoreApplication::processEvents();

    QListView *list = fd.findChild<QListView*>("listView");
    QVERIFY(list);
    QTest::keyClick(list, Qt::Key_Down);
    QTest::keyClick(list, Qt::Key_Return);
    QTest::mouseClick(list->viewport(), Qt::LeftButton, {});
    QTest::keyClick(list, Qt::Key_Down);
    QTest::keyClick(list, Qt::Key_Backspace);
    QTest::keyClick(list, Qt::Key_Down);
    QTest::keyClick(list, Qt::Key_Down);
    QTest::keyClick(list, Qt::Key_Return);
    QCOMPARE(fd.isVisible(), true);
    file.close();
    file2.close();
    file.remove();
    file2.remove();
    current.rmdir("test");
    current.rmdir("test2");
}
#endif

#if defined(Q_OS_WIN)
void tst_QFileDialog2::task226366_lowerCaseHardDriveWindows()
{
    QFileDialog fd;
    fd.setDirectory(QDir::root().path());
    fd.show();
    QLineEdit *edit = fd.findChild<QLineEdit*>("fileNameEdit");
    QToolButton *buttonParent = fd.findChild<QToolButton*>("toParentButton");
    QTest::qWait(200);
    QTest::mouseClick(buttonParent, Qt::LeftButton, {}, QPoint(0, 0));
    QTest::qWait(2000);
    QTest::keyClick(edit, Qt::Key_C);
    QTest::qWait(200);
    QTest::keyClick(edit->completer()->popup(), Qt::Key_Down);
    QTest::qWait(200);
    QCOMPARE(edit->text(), QString("C:/"));
    QTest::qWait(2000);
    //i clear my previous selection in the completer
    QTest::keyClick(edit->completer()->popup(), Qt::Key_Down);
    edit->clear();
    QTest::keyClick(edit, Qt::Key_C, Qt::ShiftModifier);
    QTest::qWait(200);
    QTest::keyClick(edit->completer()->popup(), Qt::Key_Down);
    QCOMPARE(edit->text(), QString("C:/"));
}
#endif

void tst_QFileDialog2::completionOnLevelAfterRoot()
{
    QFileDialog fd;
#if defined(Q_OS_WIN)
    fd.setDirectory("C:/");
    QDir current = fd.directory();
    QStringList entryList = current.entryList(QStringList(), QDir::Dirs);
    // Find a suitable test dir under c:-root:
    // - At least 6 characters long
    // - Ascii, letters only
    // - No another dir with same start
    QString testDir;
    foreach (const QString &entry, entryList) {
        if (entry.size() > 5 && QString(entry.toLatin1()).compare(entry) == 0) {
            bool invalid = false;
            for (int i = 0; i < 5; i++) {
                if (!entry.at(i).isLetter()) {
                    invalid = true;
                    break;
                }
            }
            if (!invalid) {
                foreach (const QString &check, entryList) {
                    if (check.startsWith(entry.left(5), Qt::CaseInsensitive) && check != entry) {
                        invalid = true;
                        break;
                    }
                }
            }
            if (!invalid) {
                testDir = entry;
                break;
            }
        }
    }
    if (testDir.isEmpty())
        QSKIP("This test requires to have a unique directory of at least six ascii characters under c:/");
#elif defined(Q_OS_ANDROID)
    // Android 11 and above doesn't allow accessing root filesystem as before,
    // so let's opt int for the app's home.
    const auto homePaths = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
    QVERIFY(!homePaths.isEmpty());
    fd.setFilter(QDir::Hidden | QDir::AllDirs | QDir::Files | QDir::System);
    fd.setDirectory(homePaths.first());
    QDir(homePaths.first()).mkdir("etc");
    auto cleanup = qScopeGuard([&]() {
        QDir(homePaths.first()).rmdir("etc");
    });
#else
    fd.setFilter(QDir::Hidden | QDir::AllDirs | QDir::Files | QDir::System);
    fd.setDirectory("/");
    QDir etc("/etc");
    if (!etc.exists())
        QSKIP("This test requires to have an etc directory under /");
#endif
    fd.show();
    QLineEdit *edit = fd.findChild<QLineEdit*>("fileNameEdit");
    QTest::qWait(2000);
#if defined(Q_OS_WIN)
    //I love testlib :D
    for (int i = 0; i < 5; i++)
        QTest::keyClick(edit, testDir.at(i).toLower().toLatin1() - 'a' + Qt::Key_A);
#else
    QTest::keyClick(edit, Qt::Key_E);
    QTest::keyClick(edit, Qt::Key_T);
#endif
    QTest::qWait(200);
    QTest::keyClick(edit->completer()->popup(), Qt::Key_Down);
    QTest::qWait(200);
#if defined(Q_OS_WIN)
    QCOMPARE(edit->text(), testDir);
#else
    QTRY_COMPARE(edit->text(), QString("etc"));
#endif
}

void tst_QFileDialog2::task233037_selectingDirectory()
{
    QDir current = QDir::currentPath();
    current.mkdir("test");
    QFileDialog fd;
    fd.setViewMode(QFileDialog::List);
    fd.setDirectory(current.absolutePath());
    fd.setAcceptMode( QFileDialog::AcceptSave);
    fd.show();
    QListView *list = fd.findChild<QListView*>("listView");
    QTest::qWait(3000); // Wait for sort to settle (I need a signal).
#ifdef QT_KEYPAD_NAVIGATION
    list->setEditFocus(true);
#endif
    QTest::keyClick(list, Qt::Key_Down);
    QDialogButtonBox *buttonBox = fd.findChild<QDialogButtonBox*>("buttonBox");
    QVERIFY(buttonBox);
    QPushButton *button = buttonBox->button(QDialogButtonBox::Save);
    QVERIFY(button);
    QVERIFY(button->isEnabled());
    current.rmdir("test");
}

void tst_QFileDialog2::task235069_hideOnEscape_data()
{
    QTest::addColumn<QString>("childName");
    QTest::addColumn<QFileDialog::ViewMode>("viewMode");
    QTest::newRow("listView") << QStringLiteral("listView") << QFileDialog::List;
    QTest::newRow("fileNameEdit") << QStringLiteral("fileNameEdit") << QFileDialog::List;
    QTest::newRow("treeView") << QStringLiteral("treeView") << QFileDialog::Detail;
}

void tst_QFileDialog2::task235069_hideOnEscape()
{
    QFETCH(QString, childName);
    QFETCH(QFileDialog::ViewMode, viewMode);
    QDir current = QDir::currentPath();

    QFileDialog fd;
    QSignalSpy spyFinished(&fd, &QDialog::finished);
    QVERIFY(spyFinished.isValid());
    QSignalSpy spyRejected(&fd, &QDialog::rejected);
    QVERIFY(spyRejected.isValid());
    fd.setViewMode(viewMode);
    fd.setDirectory(current.absolutePath());
    fd.setAcceptMode(QFileDialog::AcceptSave);
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));
    QWidget *child = fd.findChild<QWidget *>(childName);
    QVERIFY(child);
    child->setFocus();
    QTest::keyClick(child, Qt::Key_Escape);
    QCOMPARE(fd.isVisible(), false);
    QCOMPARE(spyFinished.size(), 1); // QTBUG-7690
    QCOMPARE(spyRejected.size(), 1); // reject(), don't hide()
}

#ifdef QT_BUILD_INTERNAL
void tst_QFileDialog2::task236402_dontWatchDeletedDir()
{
    //THIS TEST SHOULD NOT DISPLAY WARNINGS
    QDir current = QDir::currentPath();
    //make sure it is the first on the list
    current.mkdir("aaaaaaaaaa");
    FriendlyQFileDialog fd;
    fd.setViewMode(QFileDialog::List);
    fd.setDirectory(current.absolutePath());
    fd.setAcceptMode( QFileDialog::AcceptSave);
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));
    QListView *list = fd.findChild<QListView*>("listView");
    QVERIFY(list);
    list->setFocus();
    QTest::keyClick(list, Qt::Key_Return);
    QTest::keyClick(list, Qt::Key_Backspace);
    QTest::keyClick(list, Qt::Key_Down);
    fd.d_func()->removeDirectory(current.absolutePath() + "/aaaaaaaaaa/");
    QTest::qWait(1000);
}
#endif

void tst_QFileDialog2::task203703_returnProperSeparator()
{
    QDir current = QDir::currentPath();
    current.mkdir("aaaaaaaaaaaaaaaaaa");
    QFileDialog fd;
    fd.setDirectory(current.absolutePath());
    fd.setViewMode(QFileDialog::List);
    fd.setFileMode(QFileDialog::Directory);
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));
    QListView *list = fd.findChild<QListView*>("listView");
    QVERIFY(list);
    list->setFocus();
    QTest::keyClick(list, Qt::Key_Return);
    QDialogButtonBox *buttonBox = fd.findChild<QDialogButtonBox*>("buttonBox");
    QVERIFY(buttonBox);
    QPushButton *button = buttonBox->button(QDialogButtonBox::Cancel);
    QVERIFY(button);
    QTest::keyClick(button, Qt::Key_Return);
    QString result = fd.selectedFiles().first();
    QVERIFY(result.at(result.size() - 1) != '/');
    QVERIFY(!result.contains('\\'));
    current.rmdir("aaaaaaaaaaaaaaaaaa");
}

void tst_QFileDialog2::task228844_ensurePreviousSorting()
{
    QDir current = QDir::currentPath();
    current.mkdir("aaaaaaaaaaaaaaaaaa");
    current.cd("aaaaaaaaaaaaaaaaaa");
    current.mkdir("a");
    current.mkdir("b");
    current.mkdir("c");
    current.mkdir("d");
    current.mkdir("e");
    current.mkdir("f");
    current.mkdir("g");
    QTemporaryFile *tempFile = new QTemporaryFile(current.absolutePath() + "/rXXXXXX");
    QVERIFY2(tempFile->open(), qPrintable(tempFile->errorString()));
    current.cdUp();

    QFileDialog fd;
    fd.setDirectory(current.absolutePath());
    fd.setViewMode(QFileDialog::Detail);
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));
    QTreeView *tree = fd.findChild<QTreeView*>("treeView");
    QVERIFY(tree);
    tree->header()->setSortIndicator(3,Qt::DescendingOrder);
    QDialogButtonBox *buttonBox = fd.findChild<QDialogButtonBox*>("buttonBox");
    QVERIFY(buttonBox);
    QPushButton *button = buttonBox->button(QDialogButtonBox::Open);
    QVERIFY(button);
    QTest::mouseClick(button, Qt::LeftButton);
    QFileDialog fd2;
    fd2.setFileMode(QFileDialog::Directory);
    fd2.restoreState(fd.saveState());
    current.cd("aaaaaaaaaaaaaaaaaa");
    fd2.setDirectory(current.absolutePath());
    fd2.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd2));
    QTreeView *tree2 = fd2.findChild<QTreeView*>("treeView");
    QVERIFY(tree2);
    tree2->setFocus();

    QCOMPARE(tree2->rootIndex().data(QFileSystemModel::FilePathRole).toString(),current.absolutePath());

    QDialogButtonBox *buttonBox2 = fd2.findChild<QDialogButtonBox*>("buttonBox");
    QVERIFY(buttonBox2);
    QPushButton *button2 = buttonBox2->button(QDialogButtonBox::Open);
    QVERIFY(button2);
    fd2.selectFile("g");
    QTest::mouseClick(button2, Qt::LeftButton);
    QCOMPARE(fd2.selectedFiles().first(), current.absolutePath() + QLatin1String("/g"));

    QFileDialog fd3(0, "This is a third file dialog", tempFile->fileName());
    fd3.restoreState(fd.saveState());
    fd3.setFileMode(QFileDialog::Directory);
    fd3.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd3));
    QTreeView *tree3 = fd3.findChild<QTreeView*>("treeView");
    QVERIFY(tree3);
    tree3->setFocus();

    QCOMPARE(tree3->rootIndex().data(QFileSystemModel::FilePathRole).toString(), current.absolutePath());

    QDialogButtonBox *buttonBox3 = fd3.findChild<QDialogButtonBox*>("buttonBox");
    QVERIFY(buttonBox3);
    QPushButton *button3 = buttonBox3->button(QDialogButtonBox::Open);
    QVERIFY(button3);
    QTest::mouseClick(button3, Qt::LeftButton);
    QCOMPARE(fd3.selectedFiles().first(), tempFile->fileName());

    current.cd("aaaaaaaaaaaaaaaaaa");
    current.rmdir("a");
    current.rmdir("b");
    current.rmdir("c");
    current.rmdir("d");
    current.rmdir("e");
    current.rmdir("f");
    current.rmdir("g");
    tempFile->close();
    delete tempFile;
    current.cdUp();
    current.rmdir("aaaaaaaaaaaaaaaaaa");
}


void tst_QFileDialog2::task239706_editableFilterCombo()
{
    QFileDialog d;
    d.setNameFilter("*.cpp *.h");
    d.show();
    QVERIFY(QTest::qWaitForWindowExposed(&d));

    QList<QComboBox *> comboList = d.findChildren<QComboBox *>();
    QComboBox *filterCombo = nullptr;
    foreach (QComboBox *combo, comboList) {
        if (combo->objectName() == QString("fileTypeCombo")) {
            filterCombo = combo;
            break;
        }
    }
    QVERIFY(filterCombo);
    filterCombo->setEditable(true);
    QTest::mouseClick(filterCombo, Qt::LeftButton);
    QTest::keyPress(filterCombo, Qt::Key_X);
    QTest::keyPress(filterCombo, Qt::Key_Enter); // should not trigger assertion failure
}

void tst_QFileDialog2::task218353_relativePaths()
{
    QDir appDir = QDir::current();
    QVERIFY(appDir.cdUp() != false);
    QFileDialog d(0, "TestDialog", "..");
    QCOMPARE(d.directory().absolutePath(), appDir.absolutePath());

    d.setDirectory(appDir.absolutePath() + QLatin1String("/non-existing-directory/../another-non-existing-dir/../"));
    QCOMPARE(d.directory().absolutePath(), appDir.absolutePath());

    QDir::current().mkdir("test");
    appDir = QDir::current();
    d.setDirectory(appDir.absolutePath() + QLatin1String("/test/../test/../"));
    QCOMPARE(d.directory().absolutePath(), appDir.absolutePath());
    appDir.rmdir("test");
}

#ifdef QT_BUILD_INTERNAL
void tst_QFileDialog2::task251321_sideBarHiddenEntries()
{
    QFileDialog fd;

    QDir current = QDir::currentPath();
    current.mkdir(".hidden");
    QDir hiddenDir = QDir(".hidden");
    hiddenDir.mkdir("subdir");
    QDir hiddenSubDir = QDir(".hidden/subdir");
    hiddenSubDir.mkdir("happy");
    hiddenSubDir.mkdir("happy2");

    QList<QUrl> urls;
    urls << QUrl::fromLocalFile(hiddenSubDir.absolutePath());
    fd.setSidebarUrls(urls);
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));

    QSidebar *sidebar = fd.findChild<QSidebar*>("sidebar");
    QVERIFY(sidebar);
    sidebar->setFocus();
    sidebar->selectUrl(QUrl::fromLocalFile(hiddenSubDir.absolutePath()));
    QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, {},
                      sidebar->visualRect(sidebar->model()->index(0, 0)).center());
    // give the background processes more time on windows mobile
    QTest::qWait(250);

    QFileSystemModel *model = fd.findChild<QFileSystemModel*>("qt_filesystem_model");
    QCOMPARE(model->rowCount(model->index(hiddenSubDir.absolutePath())), 2);

    hiddenSubDir.rmdir("happy2");
    hiddenSubDir.rmdir("happy");
    hiddenDir.rmdir("subdir");
    current.rmdir(".hidden");
}
#endif

#if defined QT_BUILD_INTERNAL
class MyQSideBar : public QSidebar
{
public :
    MyQSideBar(QWidget *parent = nullptr) : QSidebar(parent)
    {}

    void removeSelection() {
        QList<QModelIndex> idxs = selectionModel()->selectedIndexes();
        QList<QPersistentModelIndex> indexes;
        for (int i = 0; i < idxs.size(); i++)
            indexes.append(idxs.at(i));

        for (int i = 0; i < indexes.size(); ++i)
            if (!indexes.at(i).data(Qt::UserRole + 1).toUrl().path().isEmpty())
                model()->removeRow(indexes.at(i).row());
    }
};
#endif

#ifdef QT_BUILD_INTERNAL
void tst_QFileDialog2::task251341_sideBarRemoveEntries()
{
    QFileDialog fd;

    QDir current = QDir::currentPath();
    current.mkdir("testDir");
    QDir testSubDir = QDir("testDir");

    QList<QUrl> urls;
    urls << QUrl::fromLocalFile(testSubDir.absolutePath());
    urls << QUrl::fromLocalFile("NotFound");
    fd.setSidebarUrls(urls);
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));

    QSidebar *sidebar = fd.findChild<QSidebar*>("sidebar");
    QVERIFY(sidebar);
    sidebar->setFocus();
    //We enter in the first bookmark
    sidebar->selectUrl(QUrl::fromLocalFile(testSubDir.absolutePath()));
    QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, {},
                      sidebar->visualRect(sidebar->model()->index(0, 0)).center());

    QFileSystemModel *model = fd.findChild<QFileSystemModel*>("qt_filesystem_model");
    QVERIFY(model);
    //There is no file
    QCOMPARE(model->rowCount(model->index(testSubDir.absolutePath())), 0);
    //Icon is not enabled QUrlModel::EnabledRole
    QVariant value = sidebar->model()->index(0, 0).data(Qt::UserRole + 2);
    QCOMPARE(qvariant_cast<bool>(value), true);

    sidebar->setFocus();
    //We enter in the second bookmark which is invalid
    sidebar->selectUrl(QUrl::fromLocalFile("NotFound"));
    QTest::mouseClick(sidebar->viewport(), Qt::LeftButton, {},
                      sidebar->visualRect(sidebar->model()->index(1, 0)).center());

    //We fallback to root because the entry in the bookmark is invalid
    QCOMPARE(model->rowCount(model->index("NotFound")), model->rowCount(model->index(model->rootPath())));
    //Icon is not enabled QUrlModel::EnabledRole
    value = sidebar->model()->index(1, 0).data(Qt::UserRole + 2);
    QCOMPARE(qvariant_cast<bool>(value), false);

    MyQSideBar mySideBar;
    mySideBar.setModelAndUrls(model, urls);
    mySideBar.show();
    mySideBar.selectUrl(QUrl::fromLocalFile(testSubDir.absolutePath()));
    QTest::qWait(1000);
    mySideBar.removeSelection();

    //We remove the first entry
    QList<QUrl> expected;
    expected << QUrl::fromLocalFile("NotFound");
    QCOMPARE(mySideBar.urls(), expected);

    mySideBar.selectUrl(QUrl::fromLocalFile("NotFound"));
    mySideBar.removeSelection();

    //We remove the second entry
    expected.clear();
    QCOMPARE(mySideBar.urls(), expected);

    current.rmdir("testDir");
}
#endif

void tst_QFileDialog2::task254490_selectFileMultipleTimes()
{
    QString tempPath = tempDir.path();
    QTemporaryFile *t;
    t = new QTemporaryFile;
    QVERIFY2(t->open(), qPrintable(t->errorString()));
    t->open();
    QFileDialog fd(0, "TestFileDialog");

    fd.setDirectory(tempPath);
    fd.setViewMode(QFileDialog::List);
    fd.setAcceptMode(QFileDialog::AcceptSave);
    fd.setFileMode(QFileDialog::AnyFile);

    //This should select the file in the QFileDialog
    fd.selectFile(t->fileName());

    //This should clear the selection and write it into the filename line edit
    fd.selectFile("new_file.txt");

    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));

    QLineEdit *lineEdit = fd.findChild<QLineEdit*>("fileNameEdit");
    QVERIFY(lineEdit);
    QCOMPARE(lineEdit->text(),QLatin1String("new_file.txt"));
    QListView *list = fd.findChild<QListView*>("listView");
    QVERIFY(list);
    QCOMPARE(list->selectionModel()->selectedRows(0).size(), 0);

    t->deleteLater();
}

#ifdef QT_BUILD_INTERNAL
void tst_QFileDialog2::task257579_sideBarWithNonCleanUrls()
{
    QDir dir(tempDir.path());
    QLatin1String dirname("autotest_task257579");
    dir.rmdir(dirname); //makes sure it doesn't exist any more
    QVERIFY(dir.mkdir(dirname));
    QString url = dir.absolutePath() + QLatin1Char('/') + dirname + QLatin1String("/..");
    QFileDialog fd;
    fd.setSidebarUrls(QList<QUrl>() << QUrl::fromLocalFile(url));
    QSidebar *sidebar = fd.findChild<QSidebar*>("sidebar");
    QCOMPARE(sidebar->urls().size(), 1);
    QVERIFY(sidebar->urls().first().toLocalFile() != url);
    QCOMPARE(sidebar->urls().first().toLocalFile(), QDir::cleanPath(url));

#ifdef Q_OS_WIN
    QCOMPARE(sidebar->model()->index(0,0).data().toString().toLower(), dir.dirName().toLower());
#else
    QCOMPARE(sidebar->model()->index(0,0).data().toString(), dir.dirName());
#endif

    //all tests are finished, we can remove the temporary dir
    QVERIFY(dir.rmdir(dirname));
}
#endif

void tst_QFileDialog2::task259105_filtersCornerCases()
{
    QFileDialog fd(0, "TestFileDialog");
    fd.setNameFilter(QLatin1String("All Files! (*);;Text Files (*.txt)"));
    fd.setOption(QFileDialog::HideNameFilterDetails, true);
    fd.show();
    QVERIFY(QTest::qWaitForWindowExposed(&fd));

    //Extensions are hidden
    QComboBox *filters = fd.findChild<QComboBox*>("fileTypeCombo");
    QVERIFY(filters);
    QCOMPARE(filters->currentText(), QLatin1String("All Files!"));
    filters->setCurrentIndex(1);
    QCOMPARE(filters->currentText(), QLatin1String("Text Files"));

    //We should have the full names
    fd.setOption(QFileDialog::HideNameFilterDetails, false);
    filters->setCurrentIndex(0);
    QCOMPARE(filters->currentText(), QLatin1String("All Files! (*)"));
    filters->setCurrentIndex(1);
    QCOMPARE(filters->currentText(), QLatin1String("Text Files (*.txt)"));

    //Corner case undocumented of the task
    fd.setNameFilter(QLatin1String("\352 (I like cheese) All Files! (*);;Text Files (*.txt)"));
    QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files! (*)"));
    filters->setCurrentIndex(1);
    QCOMPARE(filters->currentText(), QLatin1String("Text Files (*.txt)"));

    fd.setOption(QFileDialog::HideNameFilterDetails, true);
    filters->setCurrentIndex(0);
    QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files!"));
    filters->setCurrentIndex(1);
    QCOMPARE(filters->currentText(), QLatin1String("Text Files"));

    fd.setOption(QFileDialog::HideNameFilterDetails, true);
    filters->setCurrentIndex(0);
    QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files!"));
    filters->setCurrentIndex(1);
    QCOMPARE(filters->currentText(), QLatin1String("Text Files"));
}

void tst_QFileDialog2::QTBUG4419_lineEditSelectAll()
{
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
        QSKIP("Window activation is not supported");

    QString tempPath = tempDir.path();
    QTemporaryFile temporaryFile(tempPath + "/tst_qfiledialog2_lineEditSelectAll.XXXXXX");
    QVERIFY2(temporaryFile.open(), qPrintable(temporaryFile.errorString()));
    QFileDialog fd(0, "TestFileDialog", temporaryFile.fileName());

    fd.setDirectory(tempPath);
    fd.setViewMode(QFileDialog::List);
    fd.setAcceptMode(QFileDialog::AcceptSave);
    fd.setFileMode(QFileDialog::AnyFile);

    fd.show();
    fd.activateWindow();
    QVERIFY(QTest::qWaitForWindowActive(&fd));
    QCOMPARE(fd.isVisible(), true);
    QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd));

    QLineEdit *lineEdit = fd.findChild<QLineEdit*>("fileNameEdit");
    QVERIFY(lineEdit);

    QTRY_COMPARE(tempPath + QChar('/') + lineEdit->text(), temporaryFile.fileName());
    QCOMPARE(tempPath + QChar('/') + lineEdit->selectedText(), temporaryFile.fileName());
}

void tst_QFileDialog2::QTBUG6558_showDirsOnly()
{
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
        QSKIP("Window activation is not supported");

    const QString tempPath = tempDir.path();
    QDir dirTemp(tempPath);
    const QString tempName = QLatin1String("showDirsOnly.") + QString::number(QRandomGenerator::global()->generate());
    dirTemp.mkdir(tempName);
    dirTemp.cd(tempName);
    QTRY_VERIFY(dirTemp.exists());

    const QString dirPath = dirTemp.absolutePath();
    QDir dir(dirPath);

    //We create two dirs
    dir.mkdir("a");
    dir.mkdir("b");

    //Create a file
    QFile tempFile(dirPath + "/plop.txt");
    tempFile.open(QIODevice::WriteOnly | QIODevice::Text);
    QTextStream out(&tempFile);
    out << "The magic number is: " << 49 << "\n";
    tempFile.close();

    QFileDialog fd(0, "TestFileDialog");

    fd.setDirectory(dir.absolutePath());
    fd.setViewMode(QFileDialog::List);
    fd.setAcceptMode(QFileDialog::AcceptSave);
    fd.setOption(QFileDialog::ShowDirsOnly, true);
    fd.show();

    QApplicationPrivate::setActiveWindow(&fd);
    QVERIFY(QTest::qWaitForWindowActive(&fd));
    QCOMPARE(fd.isVisible(), true);
    QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd));

    QFileSystemModel *model = fd.findChild<QFileSystemModel*>("qt_filesystem_model");
    QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 2);

    fd.setOption(QFileDialog::ShowDirsOnly, false);
    QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 3);

    fd.setOption(QFileDialog::ShowDirsOnly, true);
    QTRY_COMPARE(model->rowCount(model->index(dir.absolutePath())), 2);
    QTRY_COMPARE(bool(fd.options() & QFileDialog::ShowDirsOnly), true);

    fd.setDirectory(QDir::homePath());

    //We remove the dirs
    dir.rmdir("a");
    dir.rmdir("b");

    //we delete the file
    tempFile.remove();

    dirTemp.cdUp();
    dirTemp.rmdir(tempName);
}

void tst_QFileDialog2::QTBUG4842_selectFilterWithHideNameFilterDetails()
{
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
        QSKIP("Window activation is not supported");

    QStringList filtersStr;
    filtersStr << "Images (*.png *.xpm *.jpg)" << "Text files (*.txt)" << "XML files (*.xml)";
    QString chosenFilterString("Text files (*.txt)");

    QFileDialog fd(0, "TestFileDialog");
    fd.setAcceptMode(QFileDialog::AcceptSave);
    fd.setOption(QFileDialog::HideNameFilterDetails, true);
    fd.setNameFilters(filtersStr);
    fd.selectNameFilter(chosenFilterString);
    fd.show();

    QApplicationPrivate::setActiveWindow(&fd);
    QVERIFY(QTest::qWaitForWindowActive(&fd));
    QCOMPARE(fd.isVisible(), true);
    QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd));

    QComboBox *filters = fd.findChild<QComboBox*>("fileTypeCombo");
    //We compare the current combobox text with the stripped version
    QCOMPARE(filters->currentText(), QString("Text files"));

    QFileDialog fd2(0, "TestFileDialog");
    fd2.setAcceptMode(QFileDialog::AcceptSave);
    fd2.setOption(QFileDialog::HideNameFilterDetails, false);
    fd2.setNameFilters(filtersStr);
    fd2.selectNameFilter(chosenFilterString);
    fd2.show();

    QApplicationPrivate::setActiveWindow(&fd2);
    QVERIFY(QTest::qWaitForWindowActive(&fd2));
    QCOMPARE(fd2.isVisible(), true);
    QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd2));

    QComboBox *filters2 = fd2.findChild<QComboBox*>("fileTypeCombo");
    //We compare the current combobox text with the non stripped version
    QCOMPARE(filters2->currentText(), chosenFilterString);

}

void tst_QFileDialog2::dontShowCompleterOnRoot()
{
    if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
        QSKIP("Window activation is not supported");

    QFileDialog fd(0, "TestFileDialog");
    fd.setAcceptMode(QFileDialog::AcceptSave);
    fd.show();

    QApplicationPrivate::setActiveWindow(&fd);
    QVERIFY(QTest::qWaitForWindowActive(&fd));
    QCOMPARE(fd.isVisible(), true);
    QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd));

    fd.setDirectory("");
    QLineEdit *lineEdit = fd.findChild<QLineEdit*>("fileNameEdit");
    QTRY_VERIFY(lineEdit->text().isEmpty());

    //The gatherer thread will then return the result
    QApplication::processEvents();

    QTRY_VERIFY(lineEdit->completer()->popup()->isHidden());
}

void tst_QFileDialog2::nameFilterParsing_data()
{
    QTest::addColumn<QString>("filterString");
    QTest::addColumn<QStringList>("filters");

    // QTBUG-47923: Do not trip over "*,v".
    QTest::newRow("text") << "plain text document (*.txt *.asc *,v *.doc)"
        << (QStringList() << "*.txt" << "*.asc" << "*,v" << "*.doc");
    QTest::newRow("html") << "HTML document (*.html *.htm)"
       << (QStringList() << "*.html" <<  "*.htm");
}

void tst_QFileDialog2::nameFilterParsing()
{
    QFETCH(QString, filterString);
    QFETCH(QStringList, filters);
    QCOMPARE(QPlatformFileDialogHelper::cleanFilterList(filterString), filters);
}

QTEST_MAIN(tst_QFileDialog2)
#include "tst_qfiledialog2.moc"
