Wrap flipper client methods to avoid crash

Summary: Wraps flipper client methods to avoid crash. Also added a tests which makes sure that malcious plugin cannot cause a crash

Reviewed By: jknoxville

Differential Revision: D13153277

fbshipit-source-id: ac21731fa3c4eb447f189e61f61b9e83aad91e13
This commit is contained in:
Pritesh Nandgaonkar
2018-11-26 07:52:13 -08:00
committed by Facebook Github Bot
parent 37c973d0c9
commit bf3be3e16c
4 changed files with 248 additions and 154 deletions

View File

@@ -1,23 +1,22 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
* Copyright (c) Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*
*/
#include "FlipperClient.h"
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <vector>
#include "ConnectionContextStore.h"
#include "FlipperConnectionImpl.h"
#include "FlipperConnectionManagerImpl.h"
#include "FlipperResponderImpl.h"
#include "FlipperState.h"
#include "FlipperStep.h"
#include "FlipperConnectionManagerImpl.h"
#include "ConnectionContextStore.h"
#include "Log.h"
#include <vector>
#include <stdexcept>
#include <iostream>
#include <fstream>
#if FB_SONARKIT_ENABLED
@@ -31,8 +30,10 @@ using folly::dynamic;
void FlipperClient::init(FlipperInitConfig config) {
auto state = std::make_shared<FlipperState>();
auto context = std::make_shared<ConnectionContextStore>(config.deviceData);
kInstance =
new FlipperClient(std::make_unique<FlipperConnectionManagerImpl>(std::move(config), state, context), state);
kInstance = new FlipperClient(
std::make_unique<FlipperConnectionManagerImpl>(
std::move(config), state, context),
state);
}
FlipperClient* FlipperClient::instance() {
@@ -41,16 +42,18 @@ FlipperClient* FlipperClient::instance() {
void FlipperClient::setStateListener(
std::shared_ptr<FlipperStateUpdateListener> stateListener) {
log("Setting state listener");
flipperState_->setUpdateListener(stateListener);
performAndReportError([this, &stateListener]() {
log("Setting state listener");
flipperState_->setUpdateListener(stateListener);
});
}
void FlipperClient::addPlugin(std::shared_ptr<FlipperPlugin> plugin) {
log("FlipperClient::addPlugin " + plugin->identifier());
auto step = flipperState_->start("Add plugin " + plugin->identifier());
performAndReportError([this, plugin]() {
log("FlipperClient::addPlugin " + plugin->identifier());
auto step = flipperState_->start("Add plugin " + plugin->identifier());
std::lock_guard<std::mutex> lock(mutex_);
performAndReportError([this, plugin, step]() {
std::lock_guard<std::mutex> lock(mutex_);
if (plugins_.find(plugin->identifier()) != plugins_.end()) {
throw std::out_of_range(
"plugin " + plugin->identifier() + " already added.");
@@ -64,10 +67,10 @@ void FlipperClient::addPlugin(std::shared_ptr<FlipperPlugin> plugin) {
}
void FlipperClient::removePlugin(std::shared_ptr<FlipperPlugin> plugin) {
log("FlipperClient::removePlugin " + plugin->identifier());
std::lock_guard<std::mutex> lock(mutex_);
performAndReportError([this, plugin]() {
log("FlipperClient::removePlugin " + plugin->identifier());
std::lock_guard<std::mutex> lock(mutex_);
if (plugins_.find(plugin->identifier()) == plugins_.end()) {
throw std::out_of_range("plugin " + plugin->identifier() + " not added.");
}
@@ -79,18 +82,20 @@ void FlipperClient::removePlugin(std::shared_ptr<FlipperPlugin> plugin) {
});
}
void FlipperClient::startBackgroundPlugins() {
std::cout << "Activating Background Plugins..." << std::endl;
for (std::map<std::string, std::shared_ptr<FlipperPlugin>>::iterator it=plugins_.begin(); it!=plugins_.end(); ++it) {
std::cout << it->first << std::endl;
if (it->second.get()->runInBackground()) {
auto& conn = connections_[it->first];
conn = std::make_shared<FlipperConnectionImpl>(socket_.get(),it->first);
it->second.get()->didConnect(conn);
}
void FlipperClient::startBackgroundPlugins() {
std::cout << "Activating Background Plugins..." << std::endl;
for (std::map<std::string, std::shared_ptr<FlipperPlugin>>::iterator it =
plugins_.begin();
it != plugins_.end();
++it) {
std::cout << it->first << std::endl;
if (it->second.get()->runInBackground()) {
auto& conn = connections_[it->first];
conn = std::make_shared<FlipperConnectionImpl>(socket_.get(), it->first);
it->second.get()->didConnect(conn);
}
}
}
std::shared_ptr<FlipperPlugin> FlipperClient::getPlugin(
const std::string& identifier) {
@@ -115,26 +120,28 @@ void FlipperClient::disconnect(std::shared_ptr<FlipperPlugin> plugin) {
}
void FlipperClient::refreshPlugins() {
performAndReportError([this]() {
dynamic message = dynamic::object("method", "refreshPlugins");
socket_->sendMessage(message);
});
performAndReportError([this]() {
dynamic message = dynamic::object("method", "refreshPlugins");
socket_->sendMessage(message);
});
}
void FlipperClient::onConnected() {
log("FlipperClient::onConnected");
performAndReportError([this]() {
log("FlipperClient::onConnected");
std::lock_guard<std::mutex> lock(mutex_);
connected_ = true;
startBackgroundPlugins();
std::lock_guard<std::mutex> lock(mutex_);
connected_ = true;
startBackgroundPlugins();
});
}
void FlipperClient::onDisconnected() {
log("FlipperClient::onDisconnected");
auto step = flipperState_->start("Trigger onDisconnected callbacks");
std::lock_guard<std::mutex> lock(mutex_);
connected_ = false;
performAndReportError([this, step]() {
performAndReportError([this]() {
log("FlipperClient::onDisconnected");
auto step = flipperState_->start("Trigger onDisconnected callbacks");
std::lock_guard<std::mutex> lock(mutex_);
connected_ = false;
for (const auto& iter : plugins_) {
disconnect(iter.second);
}
@@ -143,8 +150,8 @@ void FlipperClient::onDisconnected() {
}
void FlipperClient::onMessageReceived(const dynamic& message) {
std::lock_guard<std::mutex> lock(mutex_);
performAndReportError([this, &message]() {
std::lock_guard<std::mutex> lock(mutex_);
const auto& method = message["method"];
const auto& params = message.getDefault("params");
@@ -174,7 +181,8 @@ void FlipperClient::onMessageReceived(const dynamic& message) {
const auto plugin = plugins_.at(identifier);
if (!plugin.get()->runInBackground()) {
auto& conn = connections_[plugin->identifier()];
conn = std::make_shared<FlipperConnectionImpl>(socket_.get(), plugin->identifier());
conn = std::make_shared<FlipperConnectionImpl>(
socket_.get(), plugin->identifier());
plugin->didConnect(conn);
}
return;
@@ -214,20 +222,28 @@ void FlipperClient::onMessageReceived(const dynamic& message) {
responder->error(response);
});
}
void FlipperClient::performAndReportError(const std::function<void()>& func) {
#if FLIPPER_ENABLE_CRASH
// To debug the stack trace and an exception turn on the compiler flag
// FLIPPER_ENABLE_CRASH
func();
#else
try {
func();
} catch (std::exception& e) {
dynamic message = dynamic::object(
"error",
dynamic::object("message", e.what())("stacktrace", "<none>"));
dynamic message = dynamic::object(
"error", dynamic::object("message", e.what())("stacktrace", "<none>"));
if (connected_) {
socket_->sendMessage(message);
} else {
log("Error: " + std::string(e.what()));
log("Error: " + std::string(e.what()));
}
} catch (...) {
// Generic catch block for the exception of type not belonging to
// std::exception
log("Unknown error suppressed in FlipperClient");
}
#endif
}
std::string FlipperClient::getState() {

View File

@@ -1,22 +1,21 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
* Copyright (c) Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*
*/
#pragma once
#include <map>
#include <mutex>
#include <vector>
#include "FlipperConnectionImpl.h"
#include "FlipperConnectionManager.h"
#include "FlipperInitConfig.h"
#include "FlipperPlugin.h"
#include "FlipperState.h"
#include "FlipperConnectionManager.h"
#include <map>
#include <mutex>
#include "FlipperStep.h"
#include <vector>
namespace facebook {
namespace flipper {
@@ -24,23 +23,26 @@ namespace flipper {
class FlipperClient : public FlipperConnectionManager::Callbacks {
public:
/**
Call before accessing instance with FlipperClient::instance(). This will set up
all the state needed to establish a Flipper connection.
Call before accessing instance with FlipperClient::instance(). This will set
up all the state needed to establish a Flipper connection.
*/
static void init(FlipperInitConfig config);
/**
Standard accessor for the shared FlipperClient instance. This returns a
singleton instance to a shared FlipperClient. First call to this function will
create the shared FlipperClient. Must call FlipperClient::initDeviceData() before
first call to FlipperClient::instance().
singleton instance to a shared FlipperClient. First call to this function
will create the shared FlipperClient. Must call
FlipperClient::initDeviceData() before first call to
FlipperClient::instance().
*/
static FlipperClient* instance();
/**
Only public for testing
*/
FlipperClient(std::unique_ptr<FlipperConnectionManager> socket, std::shared_ptr<FlipperState> state)
FlipperClient(
std::unique_ptr<FlipperConnectionManager> socket,
std::shared_ptr<FlipperState> state)
: socket_(std::move(socket)), flipperState_(state) {
auto step = flipperState_->start("Create client");
socket_->setCallbacks(this);
@@ -48,15 +50,19 @@ class FlipperClient : public FlipperConnectionManager::Callbacks {
}
void start() {
auto step = flipperState_->start("Start client");
socket_->start();
step->complete();
performAndReportError([this]() {
auto step = flipperState_->start("Start client");
socket_->start();
step->complete();
});
}
void stop() {
auto step = flipperState_->start("Stop client");
socket_->stop();
step->complete();
performAndReportError([this]() {
auto step = flipperState_->start("Stop client");
socket_->stop();
step->complete();
});
}
void onConnected() override;
@@ -86,6 +92,7 @@ class FlipperClient : public FlipperConnectionManager::Callbacks {
}
bool hasPlugin(const std::string& identifier);
void performAndReportError(const std::function<void()>& func);
private:
static FlipperClient* instance_;
@@ -96,7 +103,6 @@ class FlipperClient : public FlipperConnectionManager::Callbacks {
std::mutex mutex_;
std::shared_ptr<FlipperState> flipperState_;
void performAndReportError(const std::function<void()>& func);
void disconnect(std::shared_ptr<FlipperPlugin> plugin);
void startBackgroundPlugins();
};

View File

@@ -1,25 +1,24 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
* Copyright (c) Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*
*/
#include "FlipperConnectionManagerImpl.h"
#include "FlipperStep.h"
#include "ConnectionContextStore.h"
#include "Log.h"
#include <folly/String.h>
#include <folly/futures/Future.h>
#include <folly/io/async/AsyncSocketException.h>
#include <folly/io/async/SSLContext.h>
#include <folly/json.h>
#include <rsocket/Payload.h>
#include <rsocket/RSocket.h>
#include <rsocket/transports/tcp/TcpConnectionFactory.h>
#include <thread>
#include <folly/io/async/AsyncSocketException.h>
#include <stdexcept>
#include <thread>
#include "ConnectionContextStore.h"
#include "FlipperStep.h"
#include "Log.h"
#define WRONG_THREAD_EXIT_MSG \
"ERROR: Aborting flipper initialization because it's not running in the flipper thread."
@@ -39,7 +38,8 @@ class ConnectionEvents : public rsocket::RSocketConnectionEvents {
FlipperConnectionManagerImpl* websocket_;
public:
ConnectionEvents(FlipperConnectionManagerImpl* websocket) : websocket_(websocket) {}
ConnectionEvents(FlipperConnectionManagerImpl* websocket)
: websocket_(websocket) {}
void onConnected() {
websocket_->isOpen_ = true;
@@ -79,11 +79,18 @@ class Responder : public rsocket::RSocketResponder {
}
};
FlipperConnectionManagerImpl::FlipperConnectionManagerImpl(FlipperInitConfig config, std::shared_ptr<FlipperState> state, std::shared_ptr<ConnectionContextStore> contextStore)
: deviceData_(config.deviceData), flipperState_(state), flipperEventBase_(config.callbackWorker), connectionEventBase_(config.connectionWorker), contextStore_(contextStore) {
CHECK_THROW(config.callbackWorker, std::invalid_argument);
CHECK_THROW(config.connectionWorker, std::invalid_argument);
}
FlipperConnectionManagerImpl::FlipperConnectionManagerImpl(
FlipperInitConfig config,
std::shared_ptr<FlipperState> state,
std::shared_ptr<ConnectionContextStore> contextStore)
: deviceData_(config.deviceData),
flipperState_(state),
flipperEventBase_(config.callbackWorker),
connectionEventBase_(config.connectionWorker),
contextStore_(contextStore) {
CHECK_THROW(config.callbackWorker, std::invalid_argument);
CHECK_THROW(config.connectionWorker, std::invalid_argument);
}
FlipperConnectionManagerImpl::~FlipperConnectionManagerImpl() {
stop();
@@ -91,10 +98,14 @@ FlipperConnectionManagerImpl::~FlipperConnectionManagerImpl() {
void FlipperConnectionManagerImpl::start() {
auto step = flipperState_->start("Start connection thread");
folly::makeFuture()
.via(flipperEventBase_->getEventBase())
.delayed(std::chrono::milliseconds(0))
.thenValue([this, step](auto&&){ step->complete(); startSync();});
.thenValue([this, step](auto&&) {
step->complete();
startSync();
});
}
void FlipperConnectionManagerImpl::startSync() {
@@ -135,7 +146,6 @@ void FlipperConnectionManagerImpl::startSync() {
}
void FlipperConnectionManagerImpl::doCertificateExchange() {
rsocket::SetupParameters parameters;
folly::SocketAddress address;
@@ -175,7 +185,8 @@ void FlipperConnectionManagerImpl::connectSecurely() {
"device_id", deviceId)("app", deviceData_.app)));
address.setFromHostPort(deviceData_.host, securePort);
std::shared_ptr<folly::SSLContext> sslContext = contextStore_->getSSLContext();
std::shared_ptr<folly::SSLContext> sslContext =
contextStore_->getSSLContext();
auto connectingSecurely = flipperState_->start("Connect securely");
connectionIsTrusted_ = true;
client_ =
@@ -198,7 +209,7 @@ void FlipperConnectionManagerImpl::reconnect() {
folly::makeFuture()
.via(flipperEventBase_->getEventBase())
.delayed(std::chrono::seconds(reconnectIntervalSeconds))
.thenValue([this](auto&&){ startSync(); });
.thenValue([this](auto&&) { startSync(); });
}
void FlipperConnectionManagerImpl::stop() {
@@ -240,7 +251,6 @@ void FlipperConnectionManagerImpl::sendMessage(const folly::dynamic& message) {
}
bool FlipperConnectionManagerImpl::isCertificateExchangeNeeded() {
if (failedConnectionAttempts_ >= 2) {
return true;
}
@@ -258,59 +268,66 @@ void FlipperConnectionManagerImpl::requestSignedCertFromFlipper() {
std::string csr = contextStore_->createCertificateSigningRequest();
generatingCSR->complete();
folly::dynamic message = folly::dynamic::object("method", "signCertificate")(
"csr", csr.c_str())("destination", contextStore_->getCertificateDirectoryPath().c_str());
folly::dynamic message =
folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())(
"destination", contextStore_->getCertificateDirectoryPath().c_str());
auto gettingCert = flipperState_->start("Getting cert from desktop");
flipperEventBase_->add([this, message, gettingCert]() {
client_->getRequester()
->requestResponse(rsocket::Payload(folly::toJson(message)))
->subscribe([this, gettingCert](rsocket::Payload p) {
auto response = p.moveDataToString();
if (!response.empty()) {
folly::dynamic config = folly::parseJson(response);
contextStore_->storeConnectionConfig(config);
}
gettingCert->complete();
log("Certificate exchange complete.");
// Disconnect after message sending is complete.
// This will trigger a reconnect which should use the secure channel.
// TODO: Connect immediately, without waiting for reconnect
client_ = nullptr;
},
[this, message](folly::exception_wrapper e) {
e.handle(
[&](rsocket::ErrorWithPayload& errorWithPayload) {
std::string errorMessage = errorWithPayload.payload.moveDataToString();
if (errorMessage.compare("not implemented")) {
log("Desktop failed to provide certificates. Error from flipper desktop:\n" + errorMessage);
client_ = nullptr;
} else {
sendLegacyCertificateRequest(message);
}
->subscribe(
[this, gettingCert](rsocket::Payload p) {
auto response = p.moveDataToString();
if (!response.empty()) {
folly::dynamic config = folly::parseJson(response);
contextStore_->storeConnectionConfig(config);
}
gettingCert->complete();
log("Certificate exchange complete.");
// Disconnect after message sending is complete.
// This will trigger a reconnect which should use the secure
// channel.
// TODO: Connect immediately, without waiting for reconnect
client_ = nullptr;
},
[e](...) {
log(("Error during certificate exchange:" + e.what()).c_str());
}
);
});
[this, message](folly::exception_wrapper e) {
e.handle(
[&](rsocket::ErrorWithPayload& errorWithPayload) {
std::string errorMessage =
errorWithPayload.payload.moveDataToString();
if (errorMessage.compare("not implemented")) {
log("Desktop failed to provide certificates. Error from flipper desktop:\n" +
errorMessage);
client_ = nullptr;
} else {
sendLegacyCertificateRequest(message);
}
},
[e](...) {
log(("Error during certificate exchange:" + e.what())
.c_str());
});
});
});
failedConnectionAttempts_ = 0;
}
void FlipperConnectionManagerImpl::sendLegacyCertificateRequest(folly::dynamic message) {
void FlipperConnectionManagerImpl::sendLegacyCertificateRequest(
folly::dynamic message) {
// Desktop is using an old version of Flipper.
// Fall back to fireAndForget, instead of requestResponse.
auto sendingRequest = flipperState_->start("Sending fallback certificate request");
auto sendingRequest =
flipperState_->start("Sending fallback certificate request");
client_->getRequester()
->fireAndForget(rsocket::Payload(folly::toJson(message)))
->subscribe([this, sendingRequest]() {
sendingRequest->complete();
folly::dynamic config = folly::dynamic::object();
contextStore_->storeConnectionConfig(config);
client_ = nullptr;
});
->fireAndForget(rsocket::Payload(folly::toJson(message)))
->subscribe([this, sendingRequest]() {
sendingRequest->complete();
folly::dynamic config = folly::dynamic::object();
contextStore_->storeConnectionConfig(config);
client_ = nullptr;
});
}
bool FlipperConnectionManagerImpl::isRunningInOwnThread() {

View File

@@ -6,8 +6,8 @@
*
*/
#include <Flipper/FlipperClient.h>
#include <FlipperTestLib/FlipperPluginMock.h>
#include <FlipperTestLib/FlipperConnectionManagerMock.h>
#include <FlipperTestLib/FlipperPluginMock.h>
#include <folly/json.h>
#include <gtest/gtest.h>
@@ -33,7 +33,8 @@ TEST(FlipperClientTests, testSaneMocks) {
TEST(FlipperClientTests, testGetPlugins) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
client.start();
client.addPlugin(std::make_shared<FlipperPluginMock>("Cat"));
@@ -49,7 +50,8 @@ TEST(FlipperClientTests, testGetPlugins) {
TEST(FlipperClientTests, testGetPlugin) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
const auto catPlugin = std::make_shared<FlipperPluginMock>("Cat");
client.addPlugin(catPlugin);
@@ -62,7 +64,8 @@ TEST(FlipperClientTests, testGetPlugin) {
TEST(FlipperClientTests, testGetPluginWithDowncast) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
const auto catPlugin = std::make_shared<FlipperPluginMock>("Cat");
client.addPlugin(catPlugin);
@@ -71,7 +74,8 @@ TEST(FlipperClientTests, testGetPluginWithDowncast) {
TEST(FlipperClientTests, testRemovePlugin) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
client.start();
auto plugin = std::make_shared<FlipperPluginMock>("Test");
@@ -88,7 +92,8 @@ TEST(FlipperClientTests, testRemovePlugin) {
TEST(FlipperClientTests, testStartStop) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
client.start();
EXPECT_TRUE(socket->isOpen());
@@ -99,15 +104,16 @@ TEST(FlipperClientTests, testStartStop) {
TEST(FlipperClientTests, testConnectDisconnect) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
bool pluginConnected = false;
const auto connectionCallback = [&](std::shared_ptr<FlipperConnection> conn) {
pluginConnected = true;
};
const auto disconnectionCallback = [&]() { pluginConnected = false; };
auto plugin = std::make_shared<FlipperPluginMock>("Test", connectionCallback,
disconnectionCallback);
auto plugin = std::make_shared<FlipperPluginMock>(
"Test", connectionCallback, disconnectionCallback);
client.addPlugin(plugin);
client.start();
@@ -122,15 +128,16 @@ TEST(FlipperClientTests, testConnectDisconnect) {
TEST(FlipperClientTests, testInitDeinit) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
bool pluginConnected = false;
const auto connectionCallback = [&](std::shared_ptr<FlipperConnection> conn) {
pluginConnected = true;
};
const auto disconnectionCallback = [&]() { pluginConnected = false; };
auto plugin = std::make_shared<FlipperPluginMock>("Test", connectionCallback,
disconnectionCallback);
auto plugin = std::make_shared<FlipperPluginMock>(
"Test", connectionCallback, disconnectionCallback);
client.start();
client.addPlugin(plugin);
@@ -160,15 +167,16 @@ TEST(FlipperClientTests, testInitDeinit) {
TEST(FlipperClientTests, testRemovePluginWhenConnected) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
bool pluginConnected = false;
const auto connectionCallback = [&](std::shared_ptr<FlipperConnection> conn) {
pluginConnected = true;
};
const auto disconnectionCallback = [&]() { pluginConnected = false; };
auto plugin = std::make_shared<FlipperPluginMock>("Test", connectionCallback,
disconnectionCallback);
auto plugin = std::make_shared<FlipperPluginMock>(
"Test", connectionCallback, disconnectionCallback);
client.addPlugin(plugin);
client.start();
@@ -181,7 +189,8 @@ TEST(FlipperClientTests, testRemovePluginWhenConnected) {
TEST(FlipperClientTests, testUnhandleableMethod) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
auto plugin = std::make_shared<FlipperPluginMock>("Test");
client.addPlugin(plugin);
@@ -201,11 +210,12 @@ TEST(FlipperClientTests, testUnhandleableMethod) {
TEST(FlipperClientTests, testExecute) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
client.start();
const auto connectionCallback = [](std::shared_ptr<FlipperConnection> conn) {
const auto receiver = [](const dynamic &params,
const auto receiver = [](const dynamic& params,
std::unique_ptr<FlipperResponder> responder) {
dynamic payload = dynamic::object("message", "yes_i_hear_u");
responder->success(payload);
@@ -231,13 +241,14 @@ TEST(FlipperClientTests, testExecute) {
TEST(FlipperClientTests, testExecuteWithParams) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
const auto connectionCallback = [&](std::shared_ptr<FlipperConnection> conn) {
const auto receiver = [](const dynamic &params,
const auto receiver = [](const dynamic& params,
std::unique_ptr<FlipperResponder> responder) {
const auto &first = params["first"].asString();
const auto &second = params["second"].asString();
const auto& first = params["first"].asString();
const auto& second = params["second"].asString();
std::map<std::string, std::string> m{{"dog", "woof"}, {"cat", "meow"}};
dynamic payload = dynamic::object(first, m[first])(second, m[second]);
responder->success(payload);
@@ -264,28 +275,32 @@ TEST(FlipperClientTests, testExecuteWithParams) {
TEST(FlipperClientTests, testExceptionUnknownPlugin) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
client.start();
dynamic messageInit = dynamic::object("method", "init")(
"params", dynamic::object("plugin", "Unknown"));
socket->callbacks->onMessageReceived(messageInit);
EXPECT_EQ(socket->messages.back()["error"]["message"],
"plugin Unknown not found for method init");
EXPECT_EQ(
socket->messages.back()["error"]["message"],
"plugin Unknown not found for method init");
}
TEST(FlipperClientTests, testExceptionUnknownApi) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
client.start();
dynamic messageInit = dynamic::object("method", "execute")(
"params", dynamic::object("api", "Unknown"));
socket->callbacks->onMessageReceived(messageInit);
EXPECT_EQ(socket->messages.back()["error"]["message"],
"connection Unknown not found for method execute");
EXPECT_EQ(
socket->messages.back()["error"]["message"],
"connection Unknown not found for method execute");
}
TEST(FlipperClientTests, testBackgroundPluginActivated) {
@@ -328,6 +343,46 @@ TEST(FlipperClientTests, testNonBackgroundPluginNotActivated) {
EXPECT_FALSE(pluginConnected);
}
TEST(FlipperClientTests, testCrashInDidConnectDisConnectIsSuppressed) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
const auto connectionCallback = [&](std::shared_ptr<FlipperConnection> conn) {
throw std::runtime_error("Runtime Error in test");
};
const auto disconnectionCallback = [&]() {
throw std::runtime_error("Runtime Error in test");
};
auto plugin = std::make_shared<FlipperPluginMock>(
"Test", connectionCallback, disconnectionCallback, true);
client.addPlugin(plugin);
EXPECT_NO_FATAL_FAILURE(client.start());
EXPECT_NO_FATAL_FAILURE(client.stop());
}
TEST(
FlipperClientTests,
testNonStandardCrashInDidConnectDisConnectIsSuppressed) {
auto socket = new FlipperConnectionManagerMock;
FlipperClient client(
std::unique_ptr<FlipperConnectionManagerMock>{socket}, state);
const auto connectionCallback = [&](std::shared_ptr<FlipperConnection> conn) {
throw "Non standard exception";
};
const auto disconnectionCallback = [&]() { throw "Non standard exception"; };
auto plugin = std::make_shared<FlipperPluginMock>(
"Test", connectionCallback, disconnectionCallback, true);
client.addPlugin(plugin);
EXPECT_NO_FATAL_FAILURE(client.start());
EXPECT_NO_FATAL_FAILURE(client.stop());
}
} // namespace test
} // namespace flipper
} // namespace facebook