Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/misc clean up and add unit tests #542

Merged
merged 6 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ jobs:
working-directory: ${{github.workspace}}/build
run: cmake --build . --config ${{matrix.buildmode}} --parallel 4

- name: Local Tests
- name: Tests
working-directory: ${{github.workspace}}/build
run: ctest -j 4 -C ${{matrix.buildmode}} --output-on-failure
6 changes: 2 additions & 4 deletions src/api/common/include/exchangepublicapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <optional>
#include <string_view>

#include "cct_string.hpp"
#include "commonapi.hpp"
#include "currencycode.hpp"
#include "currencyexchangeflatset.hpp"
Expand Down Expand Up @@ -184,14 +183,13 @@ class ExchangePublic : public ExchangeBase {
ExchangePublic(std::string_view name, FiatConverter &fiatConverter, CommonAPI &commonApi,
const CoincenterInfo &coincenterInfo);

string _name;
std::string_view _name;
CachedResultVault _cachedResultVault;
FiatConverter &_fiatConverter;
CommonAPI &_commonApi;
const CoincenterInfo &_coincenterInfo;
const ExchangeConfig &_exchangeConfig;
std::mutex _tradableMarketsMutex;
std::mutex _allOrderBooksMutex;
std::recursive_mutex _publicRequestsMutex;
};
} // namespace api
} // namespace cct
10 changes: 5 additions & 5 deletions src/api/common/src/exchangepublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ std::optional<MonetaryAmount> ExchangePublic::convert(MonetaryAmount from, Curre
priceOptions.isTakerStrategy() ? ExchangeConfig::FeeType::kTaker : ExchangeConfig::FeeType::kMaker;

if (marketOrderBookMap.empty()) {
std::lock_guard<std::mutex> guard(_allOrderBooksMutex);
std::lock_guard<std::recursive_mutex> guard(_publicRequestsMutex);
marketOrderBookMap = queryAllApproximatedOrderBooks(1);
}

Expand Down Expand Up @@ -210,7 +210,7 @@ MarketsPath ExchangePublic::findMarketsPath(CurrencyCode fromCurrency, CurrencyC

// Retrieve markets if not already done
if (markets.empty()) {
std::lock_guard<std::mutex> guard(_tradableMarketsMutex);
std::lock_guard<std::recursive_mutex> guard(_publicRequestsMutex);
markets = queryTradableMarkets();
if (markets.empty()) {
log::error("No markets retrieved for {}", _name);
Expand Down Expand Up @@ -307,7 +307,7 @@ std::optional<Market> ExchangePublic::RetrieveMarket(CurrencyCode c1, CurrencyCo
}

std::optional<Market> ExchangePublic::retrieveMarket(CurrencyCode c1, CurrencyCode c2) {
std::lock_guard<std::mutex> guard(_tradableMarketsMutex);
std::lock_guard<std::recursive_mutex> guard(_publicRequestsMutex);
return RetrieveMarket(c1, c2, queryTradableMarkets());
}

Expand Down Expand Up @@ -355,7 +355,7 @@ std::optional<Market> ExchangePublic::determineMarketFromMarketStr(std::string_v
if (markets.empty()) {
// Without any currency, and because "marketStr" is returned without hyphen, there is no easy way to guess the
// currencies so we need to compare with the markets that exist
std::lock_guard<std::mutex> guard(_tradableMarketsMutex);
std::lock_guard<std::recursive_mutex> guard(_publicRequestsMutex);
markets = queryTradableMarkets();
}
const auto symbolStrSize = marketStr.size();
Expand All @@ -382,7 +382,7 @@ std::optional<Market> ExchangePublic::determineMarketFromMarketStr(std::string_v
Market ExchangePublic::determineMarketFromFilterCurrencies(MarketSet &markets, CurrencyCode filterCur1,
CurrencyCode filterCur2) {
if (markets.empty()) {
std::lock_guard<std::mutex> guard(_tradableMarketsMutex);
std::lock_guard<std::recursive_mutex> guard(_publicRequestsMutex);
markets = queryTradableMarkets();
}

Expand Down
3 changes: 2 additions & 1 deletion src/api/exchanges/include/upbitpublicapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string_view>

#include "cachedresult.hpp"
#include "cct_string.hpp"
#include "curlhandle.hpp"
#include "currencycodeset.hpp"
#include "exchangepublicapi.hpp"
Expand Down Expand Up @@ -83,7 +84,7 @@ class UpbitPublic : public ExchangePublic {
struct WithdrawalFeesFunc {
MonetaryAmountByCurrencySet operator()();

const string& _name;
std::string_view _name;
std::string_view _dataDir;
};

Expand Down
2 changes: 2 additions & 0 deletions src/api/interface/include/exchange.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Exchange {
std::string_view name() const { return apiPublic().name(); }
std::string_view keyName() const { return apiPrivate().keyName(); }

std::size_t publicExchangePos() const;

ExchangeName createExchangeName() const {
if (hasPrivateAPI()) {
return {name(), keyName()};
Expand Down
2 changes: 2 additions & 0 deletions src/api/interface/src/exchange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Exchange::Exchange(const ExchangeConfig &exchangeConfig, ExchangePublic &exchang
_exchangePrivate(std::move(exchangePrivate)),
_pExchangeConfig(std::addressof(exchangeConfig)) {}

std::size_t Exchange::publicExchangePos() const { return PublicExchangePos(name()); }

bool Exchange::canWithdraw(CurrencyCode currencyCode, const CurrencyExchangeFlatSet &currencyExchangeSet) const {
if (_pExchangeConfig->excludedCurrenciesWithdrawal().contains(currencyCode)) {
return false;
Expand Down
5 changes: 3 additions & 2 deletions src/engine/include/coincentercommand.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <cstdint>
#include <optional>
#include <type_traits>
#include <variant>
Expand Down Expand Up @@ -61,7 +62,7 @@ class CoincenterCommand {
MonetaryAmount amount() const { return _amount; }

int depth() const { return _n; }
std::optional<int> optDepth() const { return _n == -1 ? std::nullopt : std::optional<int>(_n); }
auto optDepth() const { return _n == -1 ? std::nullopt : std::optional<int>(_n); }

Market market() const { return _market; }

Expand All @@ -87,7 +88,7 @@ class CoincenterCommand {
MonetaryAmount _amount;
Market _market;
CurrencyCode _cur1, _cur2;
int _n = -1;
int32_t _n = -1;
CoincenterCommandType _type;
bool _isPercentageAmount = false;
bool _withBalanceInUse = false;
Expand Down
167 changes: 90 additions & 77 deletions src/engine/include/commandlineoptionsparser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,35 +49,28 @@ class CommandLineOptionsParser {
CommandLineOptionsParser& append(std::ranges::input_range auto&& opts) {
const auto insertedIt = _opts.insert(_opts.end(), std::ranges::begin(opts), std::ranges::end(opts));
const auto sortByFirst = [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; };

std::sort(insertedIt, _opts.end(), sortByFirst);
std::inplace_merge(_opts.begin(), insertedIt, _opts.end(), sortByFirst);

return *this;
}

OptValueType parse(std::span<const char*> groupedArguments) {
_callbacks.clear();
_callbacks.reserve(_opts.size());
auto parse(std::span<const char* const> groupedArguments) {
OptValueType data;
for (const auto& [cmdLineOption, prop] : _opts) {
_callbacks[cmdLineOption] = registerCallback(cmdLineOption, prop, data);
}

registerCallbacks(data);

const int nbArgs = static_cast<int>(groupedArguments.size());
for (int argPos = 0; argPos < nbArgs; ++argPos) {
std::string_view argStr(groupedArguments[argPos]);
if (std::ranges::none_of(_opts, [argStr](const auto& opt) { return opt.first.matches(argStr); })) {
const auto [possibleOptionIdx, minDistance] = minLevenshteinDistanceOpt(argStr);
auto existingOptionStr = _opts[possibleOptionIdx].first.fullName();

if (minDistance <= 2 ||
minDistance < static_cast<decltype(minDistance)>(std::min(argStr.size(), existingOptionStr.size()) / 2)) {
throw invalid_argument("Unrecognized command-line option '{}' - did you mean '{}'?", argStr,
existingOptionStr);
}
throw invalid_argument("Unrecognized command-line option '{}'", argStr);
if (std::ranges::none_of(_opts, [argStr](const auto& opt) { return opt.first.matches(argStr); })) {
invalidArgument(argStr);
}

for (auto& callback : _callbacks) {
callback.second(argPos, groupedArguments);
for (auto& [_, callback] : _callbacks) {
callback(argPos, groupedArguments);
}
}

Expand Down Expand Up @@ -138,7 +131,8 @@ class CommandLineOptionsParser {

private:
static constexpr std::string_view kEmptyLine =
" "
" "
" "
" ";
static constexpr int kMaxCharLine = kEmptyLine.length();

Expand All @@ -147,7 +141,7 @@ class CommandLineOptionsParser {
template <class>
friend class CommandLineOptionsParserIterator;

using CallbackType = std::function<void(int&, std::span<const char*>)>;
using CallbackType = std::function<void(int&, std::span<const char* const>)>;

[[nodiscard]] bool isOptionValue(std::string_view opt) const {
return std::ranges::none_of(_opts, [opt](const auto& cmdLineOpt) { return cmdLineOpt.first.matches(opt); });
Expand All @@ -162,71 +156,71 @@ class CommandLineOptionsParser {

CallbackType registerCallback(const CommandLineOption& commandLineOption, CommandLineOptionType prop,
OptValueType& data) {
return [this, &commandLineOption, prop, &data](int& idx, std::span<const char*> argv) {
if (commandLineOption.matches(argv[idx])) {
std::visit(overloaded{
// integral value matcher including bool
[&data, &idx, argv, &commandLineOption](std::integral auto OptValueType::*arg) {
using IntType = std::remove_reference_t<decltype(data.*arg)>;
if constexpr (std::is_same_v<IntType, bool>) {
data.*arg = true;
} else {
if (idx + 1U < argv.size()) {
std::string_view nextOpt(argv[idx + 1]);
if (IsOptionInt(nextOpt)) {
data.*arg = FromString<IntType>(nextOpt);
++idx;
return;
}
}
ThrowExpectingValueException(commandLineOption);
}
},
return [this, &commandLineOption, prop, &data](int& idx, std::span<const char* const> argv) {
if (!commandLineOption.matches(argv[idx])) {
return;
}

// CommandLineOptionalInt value matcher
[&data, &idx, argv](CommandLineOptionalInt OptValueType::*arg) {
data.*arg = CommandLineOptionalInt(CommandLineOptionalInt::State::kOptionPresent);
std::visit(overloaded{
// integral value matcher including bool
[&data, &idx, argv, &commandLineOption](std::integral auto OptValueType::*arg) {
using IntType = std::remove_reference_t<decltype(data.*arg)>;
if constexpr (std::is_same_v<IntType, bool>) {
data.*arg = true;
} else {
if (idx + 1U < argv.size()) {
std::string_view nextOpt(argv[idx + 1]);
if (IsOptionInt(nextOpt)) {
data.*arg = FromString<int>(nextOpt);
std::string_view opt(argv[idx + 1]);
if (IsOptionInt(opt)) {
data.*arg = FromString<IntType>(opt);
++idx;
return;
}
}
},

// std::string_view value matcher
[&data, &idx, argv, &commandLineOption](std::string_view OptValueType::*arg) {
if (idx + 1U < argv.size()) {
data.*arg = std::string_view(argv[idx + 1]);
++idx;
} else {
ThrowExpectingValueException(commandLineOption);
}
},

// optional std::string_view value matcher
[this, &data, &idx, argv](std::optional<std::string_view> OptValueType::*arg) {
if (idx + 1U < argv.size() && this->isOptionValue(argv[idx + 1])) {
data.*arg = std::string_view(argv[idx + 1]);
ThrowExpectingValueException(commandLineOption);
}
},

// CommandLineOptionalInt value matcher
[&data, &idx, argv](CommandLineOptionalInt OptValueType::*arg) {
data.*arg = CommandLineOptionalInt(CommandLineOptionalInt::State::kOptionPresent);
if (idx + 1U < argv.size()) {
std::string_view opt(argv[idx + 1]);
if (IsOptionInt(opt)) {
data.*arg = FromString<int>(opt);
++idx;
} else {
data.*arg = std::string_view();
}
},

// duration value matcher
[&data, &idx, argv, &commandLineOption](Duration OptValueType::*arg) {
if (idx + 1U < argv.size()) {
data.*arg = ParseDuration(argv[idx + 1]);
++idx;
} else {
ThrowExpectingValueException(commandLineOption);
}
},
},
prop);
}
}
},

// std::string_view value matcher
[&data, &idx, argv, &commandLineOption](std::string_view OptValueType::*arg) {
if (idx + 1U < argv.size()) {
data.*arg = std::string_view(argv[++idx]);
return;
}
ThrowExpectingValueException(commandLineOption);
},

// optional std::string_view value matcher
[this, &data, &idx, argv](std::optional<std::string_view> OptValueType::*arg) {
if (idx + 1U < argv.size() && this->isOptionValue(argv[idx + 1])) {
data.*arg = std::string_view(argv[idx + 1]);
++idx;
return;
}
data.*arg = std::string_view();
},

// duration value matcher
[&data, &idx, argv, &commandLineOption](Duration OptValueType::*arg) {
if (idx + 1U < argv.size()) {
data.*arg = ParseDuration(argv[++idx]);
return;
}
ThrowExpectingValueException(commandLineOption);
},
},
prop);
};
}

Expand Down Expand Up @@ -262,6 +256,17 @@ class CommandLineOptionsParser {
return lenFirstRows + 3;
}

void invalidArgument(std::string_view argStr) const {
const auto [possibleOptionIdx, minDistance] = minLevenshteinDistanceOpt(argStr);
auto existingOptionStr = _opts[possibleOptionIdx].first.fullName();

if (minDistance <= 2 ||
minDistance < static_cast<decltype(minDistance)>(std::min(argStr.size(), existingOptionStr.size()) / 2)) {
throw invalid_argument("Unrecognized command-line option '{}' - did you mean '{}'?", argStr, existingOptionStr);
}
throw invalid_argument("Unrecognized command-line option '{}'", argStr);
}

std::pair<int, int> minLevenshteinDistanceOpt(std::string_view argStr) const {
vector<int> minDistancesToFullNameOptions(_opts.size());
LevenshteinDistanceCalculator calc;
Expand All @@ -271,6 +276,14 @@ class CommandLineOptionsParser {
return {optIt - minDistancesToFullNameOptions.begin(), *optIt};
}

void registerCallbacks(OptValueType& data) {
_callbacks.reserve(_opts.size());
_callbacks.clear();
for (const auto& [cmdLineOption, prop] : _opts) {
_callbacks[cmdLineOption] = registerCallback(cmdLineOption, prop, data);
}
}

vector<CommandLineOptionWithValue> _opts;
std::unordered_map<CommandLineOption, CallbackType> _callbacks;
};
Expand Down
Loading