memgraph/tests/manual/snapshot_generation/value_generator.hpp
Marin Tomic b9c5af2568 Refactor snapshot generators to reduce code duplication
Reviewers: florijan, dgleich, mculinovic

Reviewed By: dgleich

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1311
2018-03-22 17:27:45 +01:00

108 lines
3.6 KiB
C++

#pragma once
#include <experimental/optional>
#include <random>
#include <unordered_map>
#include "glog/logging.h"
#include "json/json.hpp"
#include "storage/property_value.hpp"
namespace snapshot_generation {
// Helper class for property value generation.
class ValueGenerator {
using json = nlohmann::json;
public:
// Generates the whole property map based on the given config.
std::unordered_map<std::string, PropertyValue> MakeProperties(
const json &config) {
std::unordered_map<std::string, PropertyValue> props;
if (config.is_null()) return props;
CHECK(config.is_object()) << "Properties config must be a dict";
for (auto it = config.begin(); it != config.end(); it++) {
auto value = MakeValue(it.value());
if (value) props.emplace(it.key(), *value);
}
return props;
}
// Generates a single value based on the given config.
std::experimental::optional<PropertyValue> MakeValue(const json &config) {
if (config.is_object()) {
const std::string &type = config["type"];
const auto &param = config["param"];
if (type == "primitive") return Primitive(param);
if (type == "counter") return Counter(param);
if (type == "optional") return Optional(param);
if (type == "bernoulli") return Bernoulli(param);
if (type == "randint") return RandInt(param);
if (type == "randstring") return RandString(param);
LOG(FATAL) << "Unknown value type";
} else {
return Primitive(config);
}
}
// Returns whatever value is stored in the JSON
PropertyValue Primitive(const json &config) {
if (config.is_string()) return config.get<std::string>();
if (config.is_number_integer()) return config.get<int64_t>();
if (config.is_number_float()) return config.get<double>();
if (config.is_boolean()) return config.get<bool>();
LOG(FATAL) << "Unsupported primitive type";
}
// Increments the counter and returns its current value
int64_t Counter(const std::string &name) { return counters_[name]++; }
// Generates a random integer in range specified by JSON config
int64_t RandInt(const json &range) {
CHECK(range.is_array() && range.size() == 2)
<< "RandInt value gen config must be a list with 2 elements";
auto from = MakeValue(range[0])->Value<int64_t>();
auto to = MakeValue(range[1])->Value<int64_t>();
CHECK(from < to) << "RandInt lower range must be lesser than upper range";
return (int64_t)(rand_(gen_) * (to - from)) + from;
}
// Generates a random alphanumeric string with length specified by JSON config
std::string RandString(const json &length) {
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
int length_int = MakeValue(length)->Value<int64_t>();
std::string r_val(length_int, 'a');
for (int i = 0; i < length_int; ++i)
r_val[i] = alphanum[(int64_t)(rand_(gen_) * strlen(alphanum))];
return r_val;
}
// Returns true with given probability
bool Bernoulli(double p) { return rand_(gen_) < p; }
// Returns a value specified by config with some probability, and nullopt
// otherwise
std::experimental::optional<PropertyValue> Optional(const json &config) {
CHECK(config.is_array() && config.size() == 2)
<< "Optional value gen config must be a list with 2 elements";
return Bernoulli(config[0]) ? MakeValue(config[1])
: std::experimental::nullopt;
}
private:
std::mt19937 gen_{std::random_device{}()};
std::uniform_real_distribution<> rand_{0.0, 1.0};
std::unordered_map<std::string, int64_t> counters_;
};
} // namespace snapshot_generation