sapling/eden/fs/config/test/ConfigSettingTest.cpp
Xavier Deguillard 255b6777f9 config: add ConfigSettingTest to CMake
Summary:
The use of realpath is needed to resolve a unix-style path to a Windows style
one. This helps keep the test generic with no `#ifdef _WIN32`

Reviewed By: wez

Differential Revision: D21319019

fbshipit-source-id: b8cdd81f0afdd135849a5b850d854399cef8cef8
2020-05-09 08:46:35 -07:00

382 lines
14 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/config/EdenConfig.h"
#include <folly/experimental/TestUtil.h>
#include <folly/test/TestUtils.h>
#include <gtest/gtest.h>
#include "eden/fs/utils/PathFuncs.h"
using folly::StringPiece;
using namespace facebook::eden;
using namespace folly::string_piece_literals;
using namespace std::chrono_literals;
class ConfigSettingTest : public ::testing::Test {
protected:
static constexpr folly::StringPiece defaultDirSp_{"/DEFAULT_DIR"};
static constexpr folly::StringPiece systemConfigDirSp_{
"/SYSTEM_CONFIG_SETTING"};
static constexpr folly::StringPiece userConfigDirSp_{"/USER_CONFIG_SETTING"};
static constexpr folly::StringPiece otherDirSp_{"/OTHER_DIR"};
AbsolutePath defaultDir_;
AbsolutePath systemConfigDir_;
AbsolutePath userConfigDir_;
AbsolutePath otherDir_;
void SetUp() override {
defaultDir_ = normalizeBestEffort(defaultDirSp_);
systemConfigDir_ = normalizeBestEffort(systemConfigDirSp_);
userConfigDir_ = normalizeBestEffort(userConfigDirSp_);
otherDir_ = normalizeBestEffort(otherDirSp_);
}
};
TEST_F(ConfigSettingTest, initStateCheck) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
// Initial should be default
EXPECT_EQ(testDir.getValue(), defaultDir_);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getConfigKey(), dirKey);
}
TEST_F(ConfigSettingTest, configSetStringValue) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
std::map<std::string, std::string> attrMap;
auto rslt = testDir.setStringValue(
systemConfigDirSp_, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
EXPECT_EQ(systemConfigDir_, testDir.getStringValue());
rslt = testDir.setStringValue(
userConfigDirSp_, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), userConfigDir_);
EXPECT_EQ(userConfigDir_, testDir.getStringValue());
}
TEST_F(ConfigSettingTest, configSetAssign) {
// Setup our target copy
auto otherKey = "otherKey"_sp;
ConfigSetting<AbsolutePath> copyOfTestDir{otherKey, otherDir_, nullptr};
// Check the copy states first, so we know where starting point is.
EXPECT_EQ(copyOfTestDir.getConfigKey(), otherKey);
EXPECT_EQ(copyOfTestDir.getSource(), ConfigSource::Default);
EXPECT_EQ(copyOfTestDir.getValue(), otherDir_);
auto dirKey = "dirKey"_sp;
{
// Setup the copy source - sufficiently different
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
std::map<std::string, std::string> attrMap;
auto rslt = testDir.setStringValue(
systemConfigDirSp_, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getConfigKey(), dirKey);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
copyOfTestDir.copyFrom(testDir);
}
// Check all attributes copied.
EXPECT_EQ(copyOfTestDir.getConfigKey(), dirKey);
EXPECT_EQ(copyOfTestDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(copyOfTestDir.getValue(), systemConfigDir_);
// Check references still valid
copyOfTestDir.clearValue(ConfigSource::Default);
}
TEST_F(ConfigSettingTest, configSetInvalidStringValue) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
std::map<std::string, std::string> attrMap;
auto rslt = testDir.setStringValue(
systemConfigDirSp_, attrMap, ConfigSource::SystemConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
folly::StringPiece userConfigDir{"INVALID USER_CONFIG_SETTING"};
rslt =
testDir.setStringValue(userConfigDir, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), true);
EXPECT_EQ(
rslt.error(),
"Cannot convert value 'INVALID USER_CONFIG_SETTING' to an absolute path");
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemConfigDir_);
}
TEST_F(ConfigSettingTest, configSetEnvSubTest) {
AbsolutePath defaultDir{"/home/bob"};
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir, nullptr};
folly::StringPiece userConfigDir{"${HOME}/test_dir"};
std::map<std::string, std::string> attrMap;
attrMap["HOME"] = "/home/bob";
attrMap["USER"] = "bob";
auto rslt =
testDir.setStringValue(userConfigDir, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
AbsolutePath bobTestDir = normalizeBestEffort("/home/bob/test_dir");
EXPECT_EQ(testDir.getValue(), bobTestDir);
EXPECT_EQ(bobTestDir, testDir.getStringValue());
folly::StringPiece homeUserConfigDir{"/home/${USER}/test_dir"};
rslt = testDir.setStringValue(
homeUserConfigDir, attrMap, ConfigSource::UserConfig);
EXPECT_EQ(rslt.hasError(), false);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), bobTestDir);
EXPECT_EQ(bobTestDir, testDir.getStringValue());
}
TEST_F(ConfigSettingTest, configSettingIgnoreDefault) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
// Initial should be default
EXPECT_EQ(testDir.getValue(), defaultDir_);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
// Setting default value should be ignored
AbsolutePath notDefaultDir{"/NOT_THE_DEFAULT_DIR"};
testDir.setValue(notDefaultDir, ConfigSource::Default);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
// Clearing the default value should be ignored
testDir.clearValue(ConfigSource::Default);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
TEST_F(ConfigSettingTest, configSettingClearNonExistingSource) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
// Clear unset priorities
testDir.clearValue(ConfigSource::CommandLine);
testDir.clearValue(ConfigSource::UserConfig);
testDir.clearValue(ConfigSource::SystemConfig);
testDir.clearValue(ConfigSource::Default);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
TEST_F(ConfigSettingTest, configSettingSetAndClearTest) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
AbsolutePath systemEdenDir{"/SYSTEM_DIR"};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
// Over-ride default
testDir.setValue(systemEdenDir, ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemEdenDir);
// Clear the over-ride
testDir.clearValue(ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
TEST_F(ConfigSettingTest, configSetOverRiddenSource) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
AbsolutePath cliEdenDir{"/CLI_DIR"};
AbsolutePath systemEdenDir{"/SYSTEM_DIR"};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
// Set the highest priority item
testDir.setValue(cliEdenDir, ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Set a middle priority item (results same as above)
testDir.setValue(systemEdenDir, ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear current highest priority
testDir.clearValue(ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemEdenDir);
}
TEST_F(ConfigSettingTest, configClearOverRiddenSource) {
auto dirKey = "dirKey"_sp;
ConfigSetting<AbsolutePath> testDir{dirKey, defaultDir_, nullptr};
AbsolutePath cliEdenDir{"/CLI_DIR"};
AbsolutePath userEdenDir{"/USER_DIR"};
AbsolutePath systemEdenDir{"/SYSTEM_DIR"};
// Initially, it should be default value
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
// Set next higher over-ride priority
testDir.setValue(systemEdenDir, ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getValue(), systemEdenDir);
// Set next higher over-ride priority
testDir.setValue(userEdenDir, ConfigSource::UserConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::UserConfig);
EXPECT_EQ(testDir.getValue(), userEdenDir);
// Set next higher over-ride priority
testDir.setValue(cliEdenDir, ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear the middle priority item (no effect on source/value)
testDir.clearValue(ConfigSource::UserConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear the middle priority item (no effect on source/value)
testDir.clearValue(ConfigSource::SystemConfig);
EXPECT_EQ(testDir.getSource(), ConfigSource::CommandLine);
EXPECT_EQ(testDir.getValue(), cliEdenDir);
// Clear highest priority - back to default
testDir.clearValue(ConfigSource::CommandLine);
EXPECT_EQ(testDir.getSource(), ConfigSource::Default);
EXPECT_EQ(testDir.getValue(), defaultDir_);
}
namespace {
template <typename T, typename FieldConverter, typename ExpectedType = T>
void checkSet(
ConfigSetting<T, FieldConverter>& setting,
ExpectedType expected,
StringPiece str) {
SCOPED_TRACE(str.str());
std::map<std::string, std::string> attrMap;
auto setResult =
setting.setStringValue(str, attrMap, ConfigSource::UserConfig);
ASSERT_FALSE(setResult.hasError()) << setResult.error();
if constexpr (std::is_floating_point<T>::value) {
EXPECT_FLOAT_EQ(expected, setting.getValue());
} else {
EXPECT_EQ(expected, setting.getValue());
}
}
template <typename T, typename FieldConverter>
void checkSetError(
ConfigSetting<T, FieldConverter>& setting,
StringPiece expectedError,
StringPiece str) {
SCOPED_TRACE(str.str());
std::map<std::string, std::string> attrMap;
auto setResult =
setting.setStringValue(str, attrMap, ConfigSource::UserConfig);
ASSERT_TRUE(setResult.hasError());
EXPECT_EQ(expectedError.str(), setResult.error());
}
} // namespace
TEST_F(ConfigSettingTest, setBool) {
ConfigSetting<bool> defaultTrue{"test:value2", true, nullptr};
EXPECT_EQ(true, defaultTrue.getValue());
ConfigSetting<bool> setting{"test:value", false, nullptr};
EXPECT_EQ(false, setting.getValue());
checkSet(setting, true, "true");
checkSet(setting, true, "1");
checkSet(setting, true, "y");
checkSet(setting, true, "yes");
checkSet(setting, true, "Y");
checkSet(setting, true, "on");
EXPECT_EQ("true", setting.getStringValue());
checkSet(setting, false, "n");
checkSet(setting, false, "0");
checkSet(setting, false, "false");
checkSet(setting, false, "off");
EXPECT_EQ("false", setting.getStringValue());
checkSetError(setting, "Empty input string", "");
checkSetError(setting, "Invalid value for bool: \"bogus\"", "bogus");
checkSetError(
setting,
"Non-whitespace character found after end of conversion: \"yes_and\"",
"yes_and");
}
TEST_F(ConfigSettingTest, setArithmetic) {
ConfigSetting<int> intSetting{"test:value", 1, nullptr};
EXPECT_EQ(1, intSetting.getValue());
checkSet(intSetting, 9, "9");
checkSet(intSetting, 1234, "1234");
checkSetError(intSetting, "Empty input string", "");
checkSetError(intSetting, "Invalid leading character: \"bogus\"", "bogus");
// In the future it might be nice to support parsing hexadecimal input.
checkSetError(
intSetting,
"Non-whitespace character found after end of conversion: \"0x15\"",
"0x15");
ConfigSetting<uint8_t> u8Setting{"test:value", 0, nullptr};
checkSet(u8Setting, 9, "9");
checkSetError(u8Setting, "Overflow during conversion: \"300\"", "300");
checkSetError(u8Setting, "Non-digit character found: \"-10\"", "-10");
ConfigSetting<float> floatSetting{"test:value", 0, nullptr};
checkSet(floatSetting, 123.0f, "123");
checkSet(floatSetting, 0.001f, "0.001");
checkSetError(
floatSetting,
"Non-whitespace character found after end of conversion: \"0.001.9\"",
"0.001.9");
}
TEST_F(ConfigSettingTest, setDuration) {
ConfigSetting<std::chrono::nanoseconds> setting{"test:value", 5ms, nullptr};
EXPECT_EQ(5ms, setting.getValue());
checkSet(setting, 90s, "1m30s");
checkSet(setting, -90s, "-1m30s");
checkSet(setting, 42ns, "42ns");
checkSet(setting, 300s, "5m");
checkSetError(setting, "empty input string", "");
checkSetError(setting, "unknown duration unit specifier", "90");
checkSetError(setting, "non-digit character found", "bogus");
}