2015-10-29 19:34:36 +01:00
|
|
|
#pragma once
|
|
|
|
#ifndef MESSMER_CRYFS_TEST_CLI_TESTUTILS_CLITEST_H
|
|
|
|
#define MESSMER_CRYFS_TEST_CLI_TESTUTILS_CLITEST_H
|
|
|
|
|
2018-09-07 14:00:47 +02:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#include <codecvt>
|
|
|
|
#include <dokan/dokan.h>
|
|
|
|
#endif
|
|
|
|
|
2016-02-11 16:39:42 +01:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <gmock/gmock.h>
|
|
|
|
#include <cpp-utils/tempfile/TempDir.h>
|
|
|
|
#include <cpp-utils/tempfile/TempFile.h>
|
2016-02-23 21:07:23 +01:00
|
|
|
#include <cryfs-cli/Cli.h>
|
|
|
|
#include <cryfs-cli/VersionChecker.h>
|
2016-02-11 16:39:42 +01:00
|
|
|
#include <cpp-utils/logging/logging.h>
|
|
|
|
#include <cpp-utils/process/subprocess.h>
|
|
|
|
#include <cpp-utils/network/FakeHttpClient.h>
|
2016-02-23 21:07:23 +01:00
|
|
|
#include "../../cryfs/testutils/MockConsole.h"
|
2017-09-30 10:18:46 +02:00
|
|
|
#include "../../cryfs/testutils/TestWithFakeHomeDirectory.h"
|
2018-02-02 01:08:01 +01:00
|
|
|
#include <cryfs/ErrorCodes.h>
|
|
|
|
#include <cpp-utils/testutils/CaptureStderrRAII.h>
|
2018-09-07 14:00:47 +02:00
|
|
|
#include <regex>
|
|
|
|
#include <string>
|
2015-10-29 19:34:36 +01:00
|
|
|
|
2017-09-30 10:18:46 +02:00
|
|
|
class CliTest : public ::testing::Test, TestWithFakeHomeDirectory {
|
2015-10-29 19:34:36 +01:00
|
|
|
public:
|
2016-01-25 15:01:34 +01:00
|
|
|
CliTest(): _basedir(), _mountdir(), basedir(_basedir.path()), mountdir(_mountdir.path()), logfile(), configfile(false), console(std::make_shared<MockConsole>()) {}
|
2015-10-29 19:34:36 +01:00
|
|
|
|
2015-10-30 17:23:08 +01:00
|
|
|
cpputils::TempDir _basedir;
|
|
|
|
cpputils::TempDir _mountdir;
|
|
|
|
boost::filesystem::path basedir;
|
|
|
|
boost::filesystem::path mountdir;
|
2015-10-29 19:34:36 +01:00
|
|
|
cpputils::TempFile logfile;
|
|
|
|
cpputils::TempFile configfile;
|
2016-01-25 15:01:34 +01:00
|
|
|
std::shared_ptr<MockConsole> console;
|
2015-10-29 19:34:36 +01:00
|
|
|
|
2017-09-30 23:24:33 +02:00
|
|
|
cpputils::unique_ref<cpputils::HttpClient> _httpClient() {
|
|
|
|
cpputils::unique_ref<cpputils::FakeHttpClient> httpClient = cpputils::make_unique_ref<cpputils::FakeHttpClient>();
|
2016-01-28 18:55:26 +01:00
|
|
|
httpClient->addWebsite("https://www.cryfs.org/version_info.json", "{\"version_info\":{\"current\":\"0.8.5\"}}");
|
2017-10-08 17:14:51 +02:00
|
|
|
return std::move(httpClient);
|
2016-01-28 18:55:26 +01:00
|
|
|
}
|
|
|
|
|
2018-09-07 07:44:23 +02:00
|
|
|
int run(const std::vector<std::string>& args) {
|
|
|
|
std::vector<const char*> _args;
|
|
|
|
_args.reserve(args.size() + 1);
|
|
|
|
_args.emplace_back("cryfs");
|
|
|
|
for (const std::string& arg : args) {
|
|
|
|
_args.emplace_back(arg.c_str());
|
|
|
|
}
|
2015-11-03 21:22:35 +01:00
|
|
|
auto &keyGenerator = cpputils::Random::PseudoRandom();
|
2018-09-04 01:51:59 +02:00
|
|
|
ON_CALL(*console, askPassword(testing::StrEq("Password: "))).WillByDefault(testing::Return("pass"));
|
|
|
|
ON_CALL(*console, askPassword(testing::StrEq("Confirm Password: "))).WillByDefault(testing::Return("pass"));
|
2016-02-13 15:06:28 +01:00
|
|
|
// Run Cryfs
|
2018-02-02 01:21:51 +01:00
|
|
|
return cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data(), _httpClient());
|
2015-10-29 19:34:36 +01:00
|
|
|
}
|
|
|
|
|
2018-09-07 07:44:23 +02:00
|
|
|
void EXPECT_EXIT_WITH_HELP_MESSAGE(const std::vector<std::string>& args, const std::string &message, cryfs::ErrorCode errorCode) {
|
2018-02-02 01:08:01 +01:00
|
|
|
EXPECT_RUN_ERROR(args, (".*Usage:.*"+message).c_str(), errorCode);
|
2015-10-29 19:34:36 +01:00
|
|
|
}
|
|
|
|
|
2018-09-07 07:44:23 +02:00
|
|
|
void EXPECT_RUN_ERROR(const std::vector<std::string>& args, const char* message, cryfs::ErrorCode errorCode) {
|
2018-09-08 04:54:01 +02:00
|
|
|
FilesystemOutput filesystem_output = _run_filesystem(args, boost::none);
|
|
|
|
|
|
|
|
EXPECT_EQ(exitCode(errorCode), filesystem_output.exit_code);
|
|
|
|
EXPECT_TRUE(std::regex_search(filesystem_output.stderr_, std::regex(message, std::regex::basic)));
|
2015-10-29 19:34:36 +01:00
|
|
|
}
|
|
|
|
|
2018-09-07 07:44:23 +02:00
|
|
|
void EXPECT_RUN_SUCCESS(const std::vector<std::string>& args, const boost::filesystem::path &mountDir) {
|
2015-11-03 22:02:04 +01:00
|
|
|
//TODO Make this work when run in background
|
|
|
|
ASSERT(std::find(args.begin(), args.end(), string("-f")) != args.end(), "Currently only works if run in foreground");
|
2018-09-08 04:54:01 +02:00
|
|
|
|
|
|
|
FilesystemOutput filesystem_output = _run_filesystem(args, mountDir);
|
|
|
|
|
|
|
|
EXPECT_EQ(0, filesystem_output.exit_code);
|
|
|
|
EXPECT_TRUE(std::regex_search(filesystem_output.stdout_, std::regex("Mounting filesystem")));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FilesystemOutput final {
|
|
|
|
int exit_code;
|
|
|
|
std::string stdout_;
|
|
|
|
std::string stderr_;
|
|
|
|
};
|
|
|
|
|
|
|
|
FilesystemOutput _run_filesystem(const std::vector<std::string>& args, const boost::optional<boost::filesystem::path>& mountDirForUnmounting) {
|
2018-09-08 12:32:46 +02:00
|
|
|
testing::internal::CaptureStdout();
|
|
|
|
testing::internal::CaptureStderr();
|
|
|
|
std::future<int> exit_code = std::async(std::launch::async, [this, &args] {
|
|
|
|
return run(args);
|
2018-09-08 04:54:01 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
if (mountDirForUnmounting.is_initialized()) {
|
|
|
|
boost::filesystem::path mountDir = *mountDirForUnmounting;
|
|
|
|
std::future<bool> unmount_success = std::async(std::launch::async, [&mountDir] {
|
|
|
|
int returncode = -1;
|
|
|
|
while (returncode != 0) {
|
2018-09-07 14:01:21 +02:00
|
|
|
#if defined(__APPLE__)
|
2018-09-08 04:54:01 +02:00
|
|
|
returncode = cpputils::Subprocess::call(std::string("umount ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
|
2018-09-07 14:01:21 +02:00
|
|
|
#elif defined(_MSC_VER)
|
2018-09-08 04:54:01 +02:00
|
|
|
// Somehow this sleeping is needed to not deadlock. Race condition in mounting/unmounting?
|
2018-09-07 14:01:21 +02:00
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
|
|
std::wstring mountDir_ = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(mountDir.string());
|
|
|
|
BOOL success = DokanRemoveMountPoint(mountDir_.c_str());
|
|
|
|
returncode = success ? 0 : -1;
|
2017-08-24 22:58:41 +02:00
|
|
|
#else
|
2018-09-08 04:54:01 +02:00
|
|
|
returncode = cpputils::Subprocess::call(std::string("fusermount -u ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
|
2017-08-24 22:58:41 +02:00
|
|
|
#endif
|
2018-09-08 04:54:01 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
if(std::future_status::ready != unmount_success.wait_for(std::chrono::seconds(10))) {
|
2018-09-08 12:32:46 +02:00
|
|
|
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
|
|
|
testing::internal::GetCapturedStderr(); // stop capturing stderr
|
|
|
|
|
|
|
|
std::cerr << "Unmount thread didn't finish";
|
|
|
|
// The std::future destructor of a future created with std::async blocks until the future is ready.
|
|
|
|
// so, instead of causing a deadlock, rather abort
|
|
|
|
exit(EXIT_FAILURE);
|
2015-11-02 21:20:10 +01:00
|
|
|
}
|
2018-09-08 04:54:01 +02:00
|
|
|
EXPECT_TRUE(unmount_success.get()); // this also re-throws any potential exceptions
|
|
|
|
}
|
2018-09-07 14:01:21 +02:00
|
|
|
|
2018-09-08 12:32:46 +02:00
|
|
|
if(std::future_status::ready != exit_code.wait_for(std::chrono::seconds(10))) {
|
|
|
|
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
|
|
|
testing::internal::GetCapturedStderr(); // stop capturing stderr
|
|
|
|
|
|
|
|
std::cerr << "Filesystem thread didn't finish";
|
|
|
|
// The std::future destructor of a future created with std::async blocks until the future is ready.
|
|
|
|
// so, instead of causing a deadlock, rather abort
|
|
|
|
exit(EXIT_FAILURE);
|
2018-09-08 04:54:01 +02:00
|
|
|
}
|
2018-09-07 14:01:21 +02:00
|
|
|
|
2018-09-08 12:32:46 +02:00
|
|
|
return {
|
|
|
|
exit_code.get(),
|
|
|
|
testing::internal::GetCapturedStdout(),
|
|
|
|
testing::internal::GetCapturedStderr()
|
|
|
|
};
|
2015-10-29 19:34:36 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|