diff --git a/.github/workflows/windows-macos.yml b/.github/workflows/windows-macos.yml
index a004eb8bdc..846afe48cb 100644
--- a/.github/workflows/windows-macos.yml
+++ b/.github/workflows/windows-macos.yml
@@ -245,9 +245,9 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
- version: 6.2.4
+ version: 6.10.0
setup-python: false
- aqtversion: '==3.1.*'
+ aqtversion: '==3.3.*'
py7zrversion: '==0.20.*'
timeout-minutes: 20
diff --git a/BUILD.macOS.md b/BUILD.macOS.md
index f08253091e..fe45c98f5a 100644
--- a/BUILD.macOS.md
+++ b/BUILD.macOS.md
@@ -18,14 +18,14 @@ Avoid the version of cmake supplied, we need a newer one (see later).
#### Option 1: Using Qt official sources
-Install the latest stable version of Qt6 LTS (6.2.4 at the moment): .
+Install the latest stable version of Qt6 LTS (6.10.0 at the moment): .
If it tells you that XCode 5.0.0 needs to be installed, go to XCode > Preferences > Locations and make a selection in
the _Command Line Tools_ box.
Add Qt6 to your PATH environment variable, adding to your `.bash_profile` file the following line:
- export PATH=$PATH:~/Qt/6.2.4/clang_64/bin
+ export PATH=$PATH:~/Qt/6.10.0/clang_64/bin
Adjust accordingly if you customized the Qt install directory.
@@ -50,7 +50,7 @@ Building
To build with official Qt sources do:
- cmake -Bbuild -H. -GNinja -DCMAKE_PREFIX_PATH=~/Qt/6.2.4/clang_64
+ cmake -Bbuild -H. -GNinja -DCMAKE_PREFIX_PATH=~/Qt/6.10.0/clang_64
Alternatively if using Qt6 from Homebrew, do
diff --git a/BUILD.windows.md b/BUILD.windows.md
index dc7d699d6b..e721fccb9d 100644
--- a/BUILD.windows.md
+++ b/BUILD.windows.md
@@ -54,11 +54,11 @@ choco install aqt -yfd
Then specify the following options in the installation command:
```[pwsh]
-aqt install-qt windows desktop 6.2.4 win64_msvc2019_64 -O C:/Qt
+aqt install-qt windows desktop 6.10.0 win64_msvc2022_64 -O C:/Qt
```
-Alternatively, download the [qtbase archive](https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt6_624/qt.qt6.624.win64_msvc2019_64/6.2.4-0-202203140926qtbase-Windows-Windows_10_21H2-MSVC2019-Windows-Windows_10_21H2-X86_64.7z)
-and extract it to `C:\Qt` (so it ends up in `C:\Qt\6.2.4`).
+Alternatively, download the [qtbase archive](https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt6_6100/qt6_6100/qt.qt6.6100.win64_msvc2022_64/6.10.0-0-202510021201qtbase-Windows-Windows_11_24H2-MSVC2022-Windows-Windows_11_24H2-X86_64.7z)
+and extract it to `C:\Qt\6.10.0`.
### Path setup
@@ -67,7 +67,7 @@ You'll have to manually add CMake and Qt to your account's PATH variable.
Search for "Edit environment variables for your account" then edit your Path variable. Add the following:
- `C:\Program Files\CMake\bin`
-- `C:\Qt\6.2.4\msvc2019_64\bin`
+- `C:\Qt\6.10.0\bin`
### Console setup
diff --git a/README.md b/README.md
index bc000ecbf9..02bc00fe01 100644
--- a/README.md
+++ b/README.md
@@ -180,7 +180,7 @@ Please follow the platform-specific build instructions in the files below:
### Generic build tips
**Qt version compatibility**
-Multipass is tested with **Qt 6.2.4**. Newer patch versions along the 6.2 track (e.g. 6.2.8) should be fine. Newer minor versions may work, but they may cause compatibility issues.
+Multipass is tested with **Qt 6.10.0**. Newer patch versions along the 6.10 track (e.g. 6.10.4) should be fine. Newer minor versions may work, but they may cause compatibility issues.
**ARM64 and cross-compilation**
If you're using Apple Silicon (arm64) or cross-compiling, ensure that your `PATH` and `CMAKE_PREFIX_PATH` environment variables point to the correct Qt installation for your system architecture.
diff --git a/include/multipass/utils.h b/include/multipass/utils.h
index b8586f4ecf..68c0a93854 100644
--- a/include/multipass/utils.h
+++ b/include/multipass/utils.h
@@ -71,6 +71,13 @@ bool is_dir(const std::string& path);
QString backend_directory_path(const Path& path, const QString& subdirectory);
std::string contents_of(const multipass::Path& file_path);
+// path normalization: returns the lexically-normal form of the path with any trailing directory
+// separator removed. The string overloads additionally convert directory separators to generic
+// form ("/").
+std::filesystem::path normalize_path(const std::filesystem::path& path);
+std::string normalize_path(const std::string& path);
+QString normalize_path(const QString& path);
+
// filesystem mount helpers
void make_target_dir(SSHSession& session,
const std::string& root,
diff --git a/src/cert/client_cert_store.cpp b/src/cert/client_cert_store.cpp
index 2aa9b3699e..ce02a17d46 100644
--- a/src/cert/client_cert_store.cpp
+++ b/src/cert/client_cert_store.cpp
@@ -43,11 +43,8 @@ auto load_certs_from_file(const QDir& cert_dir)
QFile cert_file{path};
- if (cert_file.exists())
- {
- cert_file.open(QFile::ReadOnly);
+ if (cert_file.exists() && cert_file.open(QFile::ReadOnly))
certs = QSslCertificate::fromDevice(&cert_file);
- }
return certs;
}
diff --git a/src/client/cli/cmd/exec.cpp b/src/client/cli/cmd/exec.cpp
index 83629cacbe..fe6c2db0fc 100644
--- a/src/client/cli/cmd/exec.cpp
+++ b/src/client/cli/cmd/exec.cpp
@@ -20,8 +20,10 @@
#include
#include
+#include
namespace mp = multipass;
+namespace mpu = multipass::utils;
namespace cmd = multipass::cmd;
namespace
@@ -75,14 +77,14 @@ mp::ReturnCode cmd::Exec::run(mp::ArgParser* parser)
(!parser->executeAlias() && !parser->isSet(no_dir_mapping_option)))
{
// The host directory on which the user is executing the command.
- QString clean_exec_dir = QDir::cleanPath(QDir::current().canonicalPath());
+ QString clean_exec_dir = mpu::normalize_path(QDir::current().canonicalPath());
QStringList split_exec_dir = clean_exec_dir.split('/');
auto on_info_success = [&work_dir, &split_exec_dir](mp::InfoReply& reply) {
for (const auto& mount : reply.details(0).mount_info().mount_paths())
{
auto source_dir = QDir(QString::fromStdString(mount.source_path()));
- auto clean_source_dir = QDir::cleanPath(source_dir.absolutePath());
+ auto clean_source_dir = mpu::normalize_path(source_dir.absolutePath());
QStringList split_source_dir = clean_source_dir.split('/');
// If the directory is mounted, we need to `cd` to it in the instance before
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index ffce19331e..1dd28c86a4 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -2500,8 +2500,7 @@ try
for (const auto& path_entry : request->target_paths())
{
const auto& name = path_entry.instance_name();
- const auto target_path =
- QDir::cleanPath(QString::fromStdString(path_entry.target_path())).toStdString();
+ const auto target_path = mpu::normalize_path(path_entry.target_path());
if (operative_instances.find(name) == operative_instances.end())
{
diff --git a/src/network/url_downloader.cpp b/src/network/url_downloader.cpp
index 22ef8ada9c..aa511f8a01 100644
--- a/src/network/url_downloader.cpp
+++ b/src/network/url_downloader.cpp
@@ -249,7 +249,9 @@ void mp::URLDownloader::download_to(const QUrl& url,
auto manager{MP_NETMGRFACTORY.make_network_manager(cache_dir_path)};
QFile file{file_name};
- file.open(QIODevice::ReadWrite | QIODevice::Truncate);
+ if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate))
+ throw std::runtime_error(
+ fmt::format("unable to write to file \"{}\"", file_name.toStdString()));
auto progress_monitor = [this, &abort_download, &monitor, download_type, size](
QNetworkReply* reply,
diff --git a/src/platform/backends/qemu/linux/dnsmasq_server.cpp b/src/platform/backends/qemu/linux/dnsmasq_server.cpp
index b65659ae2f..4dbd7ab5ed 100644
--- a/src/platform/backends/qemu/linux/dnsmasq_server.cpp
+++ b/src/platform/backends/qemu/linux/dnsmasq_server.cpp
@@ -55,13 +55,16 @@ mp::DNSMasqServer::DNSMasqServer(const Path& data_dir,
subnet{subnet},
conf_file{QDir(data_dir).absoluteFilePath("dnsmasq-XXXXXX.conf")}
{
- conf_file.open();
+ if (!conf_file.open())
+ throw std::runtime_error("unable to create temporary dnsmasq conf file");
conf_file.close();
QFile dnsmasq_hosts(QDir(data_dir).filePath("dnsmasq.hosts"));
if (!dnsmasq_hosts.exists())
{
- dnsmasq_hosts.open(QIODevice::WriteOnly);
+ if (!dnsmasq_hosts.open(QIODevice::WriteOnly))
+ throw std::runtime_error(
+ fmt::format("unable to create file {}", dnsmasq_hosts.filesystemFileName()));
}
dnsmasq_cmd = make_dnsmasq_process(data_dir, bridge_name, subnet, conf_file.fileName());
diff --git a/src/utils/json_utils.cpp b/src/utils/json_utils.cpp
index 154e15fa7b..d518165d8b 100644
--- a/src/utils/json_utils.cpp
+++ b/src/utils/json_utils.cpp
@@ -59,7 +59,7 @@ void mp::JsonUtils::write_json(const QJsonObject& root, QString file_name) const
// Interprocess lock file to ensure that we can synchronize the request from
// both the daemon and the client.
- QLockFile lock(fi.filePath() + u".lock"_qs);
+ QLockFile lock(fi.filePath() + ".lock");
// Make the lock file stale after a while to avoid deadlocking
// on process crashes, etc.
diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp
index 36fffd19bf..9bba4094d6 100644
--- a/src/utils/utils.cpp
+++ b/src/utils/utils.cpp
@@ -41,6 +41,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -161,6 +162,25 @@ QDir mp::utils::base_dir(const QString& path)
return info.absoluteDir();
}
+std::filesystem::path mp::utils::normalize_path(const std::filesystem::path& path)
+{
+ auto result = path.lexically_normal();
+ // Remove the trailing slash if present.
+ if (!result.has_filename())
+ result = result.parent_path();
+ return result;
+}
+
+std::string mp::utils::normalize_path(const std::string& path)
+{
+ return normalize_path(std::filesystem::path{path}).generic_string();
+}
+
+QString mp::utils::normalize_path(const QString& path)
+{
+ return QString::fromStdString(normalize_path(path.toStdString()));
+}
+
bool mp::utils::valid_hostname(const std::string& name_string)
{
QRegularExpression matcher{
@@ -432,7 +452,9 @@ void mp::utils::check_and_create_config_file(const QString& config_file_path)
MP_UTILS.make_dir(
{},
QFileInfo{config_file_path}.dir().path()); // make sure parent dir is there
- config_file.open(QIODevice::WriteOnly);
+ if (!config_file.open(QIODevice::WriteOnly))
+ throw std::runtime_error(
+ fmt::format("Unable to create config file \"{}\"", config_file_path.toStdString()));
}
}
@@ -604,22 +626,33 @@ bool mp::Utils::is_ipv4_valid(const std::string& ipv4) const
mp::Path mp::Utils::default_mount_target(const Path& source) const
{
- return source.isEmpty()
- ? ""
- : QDir{QDir::cleanPath(source)}.dirName().prepend(QString{home_in_instance} + '/');
+ // The GUI calls this function on every update of the host mount directory, so we need to be
+ // pretty lenient with what we accept for now. In the future, the GUI should be able to handle
+ // errors from this function as an indicator that the host mount directory is invalid.
+ if (source == "")
+ return "";
+
+ auto path = mp::utils::normalize_path(std::filesystem::absolute(source.toStdString()));
+ if (!path.has_filename())
+ return "";
+ auto mount_target = home_in_instance / path.filename();
+ return QString::fromStdString(mount_target.generic_string());
}
QString mp::Utils::normalize_mount_target(QString target_mount_path) const
{
- if (QDir::isRelativePath(target_mount_path)) // rely on Qt to understand Linux paths on Windows
- target_mount_path.prepend('/').prepend(home_in_instance); // QString::prepend is fast
+ std::filesystem::path target_mount = target_mount_path.toStdString();
+ if (target_mount.is_relative())
+ target_mount = home_in_instance / target_mount;
- return QDir::cleanPath(target_mount_path);
+ target_mount = mp::utils::normalize_path(target_mount);
+ return QString::fromStdString(target_mount.generic_string());
}
bool mp::Utils::invalid_target_path(const QString& target_path) const
{
- assert(target_path == QDir::cleanPath(target_path) && "target_path must be normalized");
+ assert(target_path == mp::utils::normalize_path(target_path) &&
+ "target_path must be normalized");
static QRegularExpression matcher{
QRegularExpression::anchoredPattern("/+|/+(dev|proc|sys)(/.*)*|/+home(/*)(/ubuntu/*)*")};
diff --git a/tests/file_operations.cpp b/tests/file_operations.cpp
index 501826db6a..fbcb809a6c 100644
--- a/tests/file_operations.cpp
+++ b/tests/file_operations.cpp
@@ -34,12 +34,11 @@ namespace mpt = multipass::test;
QByteArray mpt::load(QString path)
{
QFile file(path);
- if (file.exists())
- {
- file.open(QIODevice::ReadOnly);
- return file.readAll();
- }
- throw std::invalid_argument(path.toStdString() + " does not exist");
+ if (!file.exists())
+ throw std::invalid_argument(path.toStdString() + " does not exist");
+ if (!file.open(QIODevice::ReadOnly))
+ throw std::runtime_error("failed to open " + path.toStdString());
+ return file.readAll();
}
QByteArray mpt::load_test_file(const char* file_name)
diff --git a/tests/linux/test_platform_linux.cpp b/tests/linux/test_platform_linux.cpp
index c5242340a8..45ed787ed7 100644
--- a/tests/linux/test_platform_linux.cpp
+++ b/tests/linux/test_platform_linux.cpp
@@ -531,7 +531,7 @@ TEST_F(PlatformLinux, createAliasScriptWorksUnconfined)
mp::AliasDefinition{"instance", "command", "map"}));
QFile checked_script(tmp_dir.path() + "/bin/alias_name");
- checked_script.open(QFile::ReadOnly);
+ ASSERT_TRUE(checked_script.open(QFile::ReadOnly));
EXPECT_EQ(checked_script.readLine().toStdString(), "#!/bin/sh\n");
EXPECT_EQ(checked_script.readLine().toStdString(), "\n");
@@ -559,7 +559,7 @@ TEST_F(PlatformLinux, createAliasScriptWorksConfined)
mp::AliasDefinition{"instance", "command", "map"}));
QFile checked_script(tmp_dir.path() + "/bin/alias_name");
- checked_script.open(QFile::ReadOnly);
+ ASSERT_TRUE(checked_script.open(QFile::ReadOnly));
EXPECT_EQ(checked_script.readLine().toStdString(), "#!/bin/sh\n");
EXPECT_EQ(checked_script.readLine().toStdString(), "\n");
diff --git a/tests/macos/test_platform_osx.cpp b/tests/macos/test_platform_osx.cpp
index 50ca4764cf..9f9ce6a0da 100644
--- a/tests/macos/test_platform_osx.cpp
+++ b/tests/macos/test_platform_osx.cpp
@@ -312,7 +312,7 @@ TEST(PlatformOSX, createAliasScriptWorks)
MP_PLATFORM.create_alias_script("alias_name", mp::AliasDefinition{"instance", "command"}));
QFile checked_script(tmp_dir.path() + "/bin/alias_name");
- checked_script.open(QFile::ReadOnly);
+ ASSERT_TRUE(checked_script.open(QFile::ReadOnly));
EXPECT_EQ(checked_script.readLine().toStdString(), "#!/bin/sh\n");
EXPECT_EQ(checked_script.readLine().toStdString(), "\n");
diff --git a/tests/qemu/test_qemu_vm_process_spec.cpp b/tests/qemu/test_qemu_vm_process_spec.cpp
index 1604e7e8a0..36996f6c29 100644
--- a/tests/qemu/test_qemu_vm_process_spec.cpp
+++ b/tests/qemu/test_qemu_vm_process_spec.cpp
@@ -18,6 +18,7 @@
#include "tests/common.h"
#include "tests/mock_environment_helpers.h"
+#include
#include
#include
@@ -27,6 +28,7 @@
namespace mp = multipass;
namespace mpt = multipass::test;
+namespace mpu = multipass::utils;
using namespace testing;
struct TestQemuVMProcessSpec : public Test
@@ -130,23 +132,24 @@ TEST_F(TestQemuVMProcessSpec, apparmorProfileIncludesFileMountPerms)
{
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(spec.apparmor_profile().contains("path/to/source/ rw"));
- EXPECT_TRUE(spec.apparmor_profile().contains("path/to/source/** rwlk"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(), HasSubstr("path/to/source/ rw"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(), HasSubstr("path/to/source/** rwlk"));
}
TEST_F(TestQemuVMProcessSpec, apparmorProfileHasCorrectName)
{
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(spec.apparmor_profile().contains("profile multipass.vm_name.qemu-system-"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr("profile multipass.vm_name.qemu-system-"));
}
TEST_F(TestQemuVMProcessSpec, apparmorProfileIncludesDiskImages)
{
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(spec.apparmor_profile().contains("/path/to/image rwk,"));
- EXPECT_TRUE(spec.apparmor_profile().contains("/path/to/cloud_init.iso rk,"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(), HasSubstr("/path/to/image rwk,"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(), HasSubstr("/path/to/cloud_init.iso rk,"));
}
TEST_F(TestQemuVMProcessSpec, apparmorProfileIdentifier)
@@ -165,11 +168,12 @@ TEST_F(TestQemuVMProcessSpec, apparmorProfileRunningAsSnapCorrect)
mpt::SetEnvScope e2("SNAP_NAME", snap_name);
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(
- spec.apparmor_profile().contains("signal (receive) peer=snap.multipass.multipassd"));
- EXPECT_TRUE(spec.apparmor_profile().contains(QString("%1/qemu/* r,").arg(snap_dir.path())));
- EXPECT_TRUE(
- spec.apparmor_profile().contains(QString("%1/usr/bin/qemu-system-").arg(snap_dir.path())));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr("signal (receive) peer=snap.multipass.multipassd"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr(QString("%1/qemu/* r,").arg(snap_dir.path()).toStdString()));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr(QString("%1/usr/bin/qemu-system-").arg(snap_dir.path()).toStdString()));
}
TEST_F(TestQemuVMProcessSpec, apparmorProfileRunningAsSymlinkedSnapCorrect)
@@ -184,9 +188,10 @@ TEST_F(TestQemuVMProcessSpec, apparmorProfileRunningAsSymlinkedSnapCorrect)
mpt::SetEnvScope e2("SNAP_NAME", snap_name);
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(spec.apparmor_profile().contains(QString("%1/qemu/* r,").arg(snap_dir.path())));
- EXPECT_TRUE(
- spec.apparmor_profile().contains(QString("%1/usr/bin/qemu-system-").arg(snap_dir.path())));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr(QString("%1/qemu/* r,").arg(snap_dir.path()).toStdString()));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr(QString("%1/usr/bin/qemu-system-").arg(snap_dir.path()).toStdString()));
}
TEST_F(TestQemuVMProcessSpec, apparmorProfileNotRunningAsSnapCorrect)
@@ -197,10 +202,12 @@ TEST_F(TestQemuVMProcessSpec, apparmorProfileNotRunningAsSnapCorrect)
mpt::SetEnvScope e2("SNAP_NAME", snap_name);
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(spec.apparmor_profile().contains("signal (receive) peer=unconfined"));
- EXPECT_TRUE(
- spec.apparmor_profile().contains("/usr{,/local}/share/{seabios,ovmf,qemu,qemu-efi}/* r,"));
- EXPECT_TRUE(spec.apparmor_profile().contains(" /usr/bin/qemu-system-")); // space wanted
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr("signal (receive) peer=unconfined"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr("/usr{,/local}/share/{seabios,ovmf,qemu,qemu-efi}/* r,"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr(" /usr/bin/qemu-system-")); // space wanted
}
TEST_F(TestQemuVMProcessSpec, apparmorProfileLetsBridgeHelperRunInSnap)
@@ -212,8 +219,8 @@ TEST_F(TestQemuVMProcessSpec, apparmorProfileLetsBridgeHelperRunInSnap)
mpt::SetEnvScope e2("SNAP_NAME", snap_name);
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(
- spec.apparmor_profile().contains(QString(" %1/bin/bridge_helper").arg(snap_dir.path())));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(),
+ HasSubstr(QString(" %1/bin/bridge_helper").arg(mpu::snap_dir()).toStdString()));
}
TEST_F(TestQemuVMProcessSpec, apparmorProfileLetsBridgeHelperRunOutsideSnap)
@@ -224,5 +231,5 @@ TEST_F(TestQemuVMProcessSpec, apparmorProfileLetsBridgeHelperRunOutsideSnap)
mpt::SetEnvScope e2("SNAP_NAME", snap_name);
mp::QemuVMProcessSpec spec(desc, platform_args, mount_args, std::nullopt);
- EXPECT_TRUE(spec.apparmor_profile().contains(" /bin/bridge_helper"));
+ EXPECT_THAT(spec.apparmor_profile().toStdString(), HasSubstr(" /bin/bridge_helper"));
}
diff --git a/tests/test_alias_dict.cpp b/tests/test_alias_dict.cpp
index 8d1f96ffca..9ecbe8a901 100644
--- a/tests/test_alias_dict.cpp
+++ b/tests/test_alias_dict.cpp
@@ -51,8 +51,9 @@ struct AliasDictionary : public FakeAliasConfig, public Test
TEST_F(AliasDictionary, worksWithEmptyFile)
{
QFile db(QString::fromStdString(db_filename()));
+ MP_UTILS.make_dir(QFileInfo(db).dir());
- db.open(QIODevice::ReadWrite); // Create the database file.
+ ASSERT_TRUE(db.open(QIODevice::ReadWrite)); // Create the database file.
std::stringstream trash_stream;
mpt::StubTerminal trash_term(trash_stream, trash_stream, trash_stream);
diff --git a/tests/test_cli_client.cpp b/tests/test_cli_client.cpp
index 55c250cb18..d3b3f347b5 100644
--- a/tests/test_cli_client.cpp
+++ b/tests/test_cli_client.cpp
@@ -1244,7 +1244,7 @@ TEST_F(Client, DISABLE_ON_MACOS(launchCmdCustomImageHttpOk))
TEST_F(Client, launchCmdCloudinitOptionWithValidFileIsOk)
{
QTemporaryFile tmpfile; // file is auto-deleted when this goes out of scope
- tmpfile.open();
+ ASSERT_TRUE(tmpfile.open());
tmpfile.write("password: passw0rd"); // need some YAML
tmpfile.close();
EXPECT_CALL(mock_daemon, launch(_, _));
diff --git a/tests/test_image_vault_utils.cpp b/tests/test_image_vault_utils.cpp
index 659c9f3b3e..d249d79863 100644
--- a/tests/test_image_vault_utils.cpp
+++ b/tests/test_image_vault_utils.cpp
@@ -96,7 +96,7 @@ TEST_F(TestImageVaultUtils, computeHashComputesSha256)
QByteArray data = ":)";
QBuffer buffer{&data};
- buffer.open(QIODevice::ReadOnly);
+ ASSERT_TRUE(buffer.open(QIODevice::ReadOnly));
auto hash = MP_IMAGE_VAULT_UTILS.compute_hash(buffer);
EXPECT_EQ(hash, "54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c");
diff --git a/tests/test_new_release_monitor.cpp b/tests/test_new_release_monitor.cpp
index c453c6d0ed..0f4e4967e8 100644
--- a/tests/test_new_release_monitor.cpp
+++ b/tests/test_new_release_monitor.cpp
@@ -47,7 +47,8 @@ class StubUpdateJson
public:
StubUpdateJson(QString version, QString url)
{
- json_file.open();
+ if (!json_file.open())
+ throw std::runtime_error("test failed to create temporary file");
json_file.write(json_template.arg(url).arg(version).toUtf8());
json_file.close();
}
diff --git a/tests/test_url_downloader.cpp b/tests/test_url_downloader.cpp
index 21ffd8fb75..dd726ceabb 100644
--- a/tests/test_url_downloader.cpp
+++ b/tests/test_url_downloader.cpp
@@ -232,7 +232,7 @@ TEST_F(URLDownloader, fileDownloadNoErrorHasExpectedResults)
QFile test_file{download_file};
ASSERT_TRUE(test_file.exists());
- test_file.open(QIODevice::ReadOnly);
+ ASSERT_TRUE(test_file.open(QIODevice::ReadOnly));
auto file_data = test_file.readAll();
EXPECT_EQ(file_data, test_data);
}
@@ -291,7 +291,7 @@ TEST_F(URLDownloader, fileDownloadErrorTriesCache)
QFile test_file{download_file};
ASSERT_TRUE(test_file.exists());
- test_file.open(QIODevice::ReadOnly);
+ ASSERT_TRUE(test_file.open(QIODevice::ReadOnly));
auto file_data = test_file.readAll();
EXPECT_EQ(file_data, test_data);
}
diff --git a/tests/test_utils.cpp b/tests/test_utils.cpp
index ab42a3788e..c642bb85db 100644
--- a/tests/test_utils.cpp
+++ b/tests/test_utils.cpp
@@ -53,7 +53,7 @@ std::string file_contents{"line 1 of file contents\nline 2\n"};
void check_file_contents(QFile& checked_file, const std::string& checked_contents)
{
- checked_file.open(QIODevice::ReadOnly | QIODevice::Text);
+ ASSERT_TRUE(checked_file.open(QIODevice::ReadOnly | QIODevice::Text));
QString actual_contents;
@@ -642,9 +642,40 @@ TEST(Utils, checkFilesystemBytesAvailableReturnsNonNegative)
EXPECT_GE(bytes_available, 0);
}
-using NormalizeMountTargetParam = std::tuple;
+using MountTargetParam = std::tuple;
-class NormalizeMountTargetTest : public TestWithParam
+class DefaultMountTargetTest : public TestWithParam
+{
+};
+
+TEST_P(DefaultMountTargetTest, mountTargetsDefaultCorrectly)
+{
+ const auto& [input, expected_output] = GetParam();
+
+ auto result = MP_UTILS.default_mount_target(QString::fromStdString(input));
+ EXPECT_EQ(result.toStdString(), expected_output);
+}
+
+std::vector default_mount_target_values = {
+ std::make_tuple("", ""),
+ std::make_tuple("/", ""),
+ std::make_tuple("/.", ""),
+ std::make_tuple("Documents", "/home/ubuntu/Documents"),
+ std::make_tuple("Documents/", "/home/ubuntu/Documents"),
+ std::make_tuple("Documents/.", "/home/ubuntu/Documents"),
+ std::make_tuple("/home/user/Desktop", "/home/ubuntu/Desktop"),
+ std::make_tuple("/home/user/Desktop/", "/home/ubuntu/Desktop"),
+ std::make_tuple("/folder/subfolder/..", "/home/ubuntu/folder"),
+#ifdef MULTIPASS_PLATFORM_WINDOWS
+ std::make_tuple(R"(Documents\)", "/home/ubuntu/Documents"),
+ std::make_tuple(R"(C:\Users\user\Documents)", "/home/ubuntu/Documents"),
+ std::make_tuple(R"(C:\Users\user\Documents\)", "/home/ubuntu/Documents"),
+#endif
+};
+
+INSTANTIATE_TEST_SUITE_P(Utils, DefaultMountTargetTest, ValuesIn(default_mount_target_values));
+
+class NormalizeMountTargetTest : public TestWithParam
{
};
@@ -656,19 +687,27 @@ TEST_P(NormalizeMountTargetTest, mountTargetsNormalizeCorrectly)
EXPECT_EQ(result.toStdString(), expected_output);
}
-INSTANTIATE_TEST_SUITE_P(
- Utils,
- NormalizeMountTargetTest,
- Values(std::make_tuple("Documents", "/home/ubuntu/Documents"),
- std::make_tuple("./folder", "/home/ubuntu/folder"),
- std::make_tuple("../folder", "/home/folder"),
- std::make_tuple("../..//folder", "/folder"),
- std::make_tuple("/usr/local/bin//.././/.//bin/././", "/usr/local/bin"),
- std::make_tuple("folder//subfolder", "/home/ubuntu/folder/subfolder"),
- std::make_tuple("./Documents/../Downloads", "/home/ubuntu/Downloads"),
- std::make_tuple("", "/home/ubuntu"),
- std::make_tuple(".", "/home/ubuntu"),
- std::make_tuple("..", "/home"),
- std::make_tuple("folder/./subfolder", "/home/ubuntu/folder/subfolder"),
- std::make_tuple("folder/../other-folder", "/home/ubuntu/other-folder"),
- std::make_tuple("//", "/")));
+std::vector normalize_mount_target_values = {
+ std::make_tuple("Documents", "/home/ubuntu/Documents"),
+ std::make_tuple("", "/home/ubuntu"),
+ std::make_tuple(".", "/home/ubuntu"),
+ std::make_tuple("..", "/home"),
+ std::make_tuple("./", "/home/ubuntu"),
+ std::make_tuple("folder/", "/home/ubuntu/folder"),
+#ifdef MULTIPASS_PLATFORM_WINDOWS
+ std::make_tuple(R"(.\)", "/home/ubuntu"),
+ std::make_tuple(R"(folder\)", "/home/ubuntu/folder"),
+ std::make_tuple(R"(folder\subfolder)", "/home/ubuntu/folder/subfolder"),
+#endif
+ std::make_tuple("./folder", "/home/ubuntu/folder"),
+ std::make_tuple("../folder", "/home/folder"),
+ std::make_tuple("../..//folder", "/folder"),
+ std::make_tuple("/usr/local/bin//.././/.//bin/././", "/usr/local/bin"),
+ std::make_tuple("folder//subfolder", "/home/ubuntu/folder/subfolder"),
+ std::make_tuple("./Documents/../Downloads", "/home/ubuntu/Downloads"),
+ std::make_tuple("folder/./subfolder", "/home/ubuntu/folder/subfolder"),
+ std::make_tuple("folder/../other-folder", "/home/ubuntu/other-folder"),
+ std::make_tuple("//", "/"),
+};
+
+INSTANTIATE_TEST_SUITE_P(Utils, NormalizeMountTargetTest, ValuesIn(normalize_mount_target_values));
diff --git a/tests/windows/test_platform_win.cpp b/tests/windows/test_platform_win.cpp
index be5b8b8bc0..e90b530762 100644
--- a/tests/windows/test_platform_win.cpp
+++ b/tests/windows/test_platform_win.cpp
@@ -615,7 +615,7 @@ TEST(PlatformWin, createAliasScriptWorks)
MP_PLATFORM.create_alias_script("alias_name", mp::AliasDefinition{"instance", "command"}));
QFile checked_script(tmp_dir.path() + "/AppData/local/multipass/bin/alias_name.bat");
- checked_script.open(QFile::ReadOnly);
+ ASSERT_TRUE(checked_script.open(QFile::ReadOnly));
std::string script_line = checked_script.readLine().toStdString();
EXPECT_THAT(script_line, HasSubstr("@"));