180 lines
5.8 KiB
C++
180 lines
5.8 KiB
C++
/*
|
|
* TesterUtil.h
|
|
*
|
|
* This source file is part of the FoundationDB open source project
|
|
*
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#ifndef APITESTER_UTIL_H
|
|
#define APITESTER_UTIL_H
|
|
|
|
#include <random>
|
|
#include <ostream>
|
|
#include <optional>
|
|
#include <fmt/format.h>
|
|
#include <chrono>
|
|
|
|
#include "test/fdb_api.hpp"
|
|
|
|
#undef ERROR
|
|
#define ERROR(name, number, description) enum { error_code_##name = number };
|
|
|
|
#include "flow/error_definitions.h"
|
|
|
|
namespace fmt {
|
|
|
|
// fmt::format formatting for std::optional<T>
|
|
template <typename T>
|
|
struct formatter<std::optional<T>> : fmt::formatter<T> {
|
|
|
|
template <typename FormatContext>
|
|
auto format(const std::optional<T>& opt, FormatContext& ctx) {
|
|
if (opt) {
|
|
fmt::formatter<T>::format(*opt, ctx);
|
|
return ctx.out();
|
|
}
|
|
return fmt::format_to(ctx.out(), "<empty>");
|
|
}
|
|
};
|
|
|
|
} // namespace fmt
|
|
|
|
namespace FdbApiTester {
|
|
|
|
fdb::ByteString lowerCase(fdb::BytesRef str);
|
|
|
|
class Random {
|
|
public:
|
|
Random();
|
|
|
|
static Random& get();
|
|
|
|
int randomInt(int min, int max);
|
|
|
|
template <class StringType>
|
|
StringType randomStringLowerCase(int minLength, int maxLength) {
|
|
int length = randomInt(minLength, maxLength);
|
|
StringType str;
|
|
str.reserve(length);
|
|
for (int i = 0; i < length; i++) {
|
|
str += (char)randomInt('a', 'z');
|
|
}
|
|
return str;
|
|
}
|
|
|
|
fdb::ByteString randomByteStringLowerCase(int minLength, int maxLength) {
|
|
return randomStringLowerCase<fdb::ByteString>(minLength, maxLength);
|
|
}
|
|
|
|
bool randomBool(double trueRatio);
|
|
|
|
std::mt19937 random;
|
|
};
|
|
|
|
class TesterError : public std::runtime_error {
|
|
public:
|
|
explicit TesterError(const char* message) : std::runtime_error(message) {}
|
|
explicit TesterError(const std::string& message) : std::runtime_error(message) {}
|
|
TesterError(const TesterError&) = default;
|
|
TesterError& operator=(const TesterError&) = default;
|
|
TesterError(TesterError&&) = default;
|
|
TesterError& operator=(TesterError&&) = default;
|
|
};
|
|
|
|
void print_internal_error(const char* msg, const char* file, int line);
|
|
|
|
#define ASSERT(condition) \
|
|
do { \
|
|
if (!(condition)) { \
|
|
print_internal_error(#condition, __FILE__, __LINE__); \
|
|
abort(); \
|
|
} \
|
|
} while (false) // For use in destructors, where throwing exceptions is extremely dangerous
|
|
|
|
using TimePoint = std::chrono::steady_clock::time_point;
|
|
using TimeDuration = std::chrono::microseconds::rep;
|
|
|
|
static inline TimePoint timeNow() {
|
|
return std::chrono::steady_clock::now();
|
|
}
|
|
|
|
static inline TimeDuration timeElapsedInUs(const TimePoint& start, const TimePoint& end) {
|
|
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
|
|
}
|
|
|
|
static inline TimeDuration timeElapsedInUs(const TimePoint& start) {
|
|
return timeElapsedInUs(start, timeNow());
|
|
}
|
|
|
|
static inline double microsecToSec(TimeDuration timeUs) {
|
|
return timeUs / 1000000.0;
|
|
}
|
|
|
|
std::optional<fdb::Value> copyValueRef(fdb::future_var::ValueRef::Type value);
|
|
|
|
using KeyValueArray = std::pair<std::vector<fdb::KeyValue>, bool>;
|
|
KeyValueArray copyKeyValueArray(fdb::future_var::KeyValueRefArray::Type array);
|
|
|
|
using KeyRangeArray = std::vector<fdb::KeyRange>;
|
|
KeyRangeArray copyKeyRangeArray(fdb::future_var::KeyRangeRefArray::Type array);
|
|
|
|
using GranuleSummaryArray = std::vector<fdb::GranuleSummary>;
|
|
GranuleSummaryArray copyGranuleSummaryArray(fdb::future_var::GranuleSummaryRefArray::Type array);
|
|
|
|
using GranuleDescriptionArray = std::vector<fdb::GranuleDescription>;
|
|
GranuleDescriptionArray copyGranuleDescriptionArray(fdb::future_var::GranuleDescriptionRefArray::Type array);
|
|
|
|
using GranuleMutationArray = std::vector<fdb::GranuleMutation>;
|
|
GranuleMutationArray copyGranuleMutationArray(fdb::future_var::GranuleMutationRefArray::Type array);
|
|
|
|
static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, "Do not support non-little-endian systems");
|
|
|
|
// Converts a little-endian encoded number into an integral type.
|
|
template <class T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
|
static T toInteger(fdb::BytesRef value) {
|
|
ASSERT(value.size() == sizeof(T));
|
|
T output;
|
|
memcpy(&output, value.data(), value.size());
|
|
return output;
|
|
}
|
|
|
|
// Converts an integral type to a little-endian encoded byte string.
|
|
template <class T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
|
static fdb::ByteString toByteString(T value) {
|
|
fdb::ByteString output(sizeof(T), 0);
|
|
memcpy(output.data(), (const uint8_t*)&value, sizeof(value));
|
|
return output;
|
|
}
|
|
|
|
// Creates a temporary file; file gets destroyed/deleted along with object destruction.
|
|
struct TmpFile {
|
|
public:
|
|
~TmpFile();
|
|
void create(std::string_view dir, std::string_view prefix);
|
|
void write(std::string_view data);
|
|
void remove();
|
|
const std::string& getFileName() const { return filename; }
|
|
|
|
private:
|
|
std::string filename;
|
|
};
|
|
|
|
} // namespace FdbApiTester
|
|
|
|
#endif
|