mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
fa180ad585
Summary: This stack updates eden to be able to check all of the locations that able users certificate may reside. There can be multiple places where a cert may reside (we cant always definitively choose one place to look based on the platform). Thus we need to be able to configure multiple locations for certs in our eden config. This makes the switch over in eden from using one place for the client cert to use and using the first available client cert from a list. NOTE: most of this diff is fixing unit tests take a look at `EdenConfig.h` and `EdenConfig.cpp` first Reviewed By: wez Differential Revision: D23359939 fbshipit-source-id: 44beecce3ef098a734dbd7c5eb3fa5f0aad6b50b
414 lines
15 KiB
C++
414 lines
15 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 "eden/fs/utils/PathFuncs.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");
|
|
}
|
|
|
|
TEST_F(ConfigSettingTest, setArray) {
|
|
ConfigSetting<std::vector<std::string>> setting{
|
|
"test:value", std::vector<std::string>{}, nullptr};
|
|
EXPECT_EQ(std::vector<std::string>{}, setting.getValue());
|
|
checkSet(setting, std::vector<std::string>{"a"}, "[\"a\"]");
|
|
checkSet(setting, std::vector<std::string>{"a", "b"}, "[\"a\", \"b\"]");
|
|
checkSet(setting, std::vector<std::string>{}, "[]");
|
|
checkSetError(
|
|
setting,
|
|
"Error parsing an array of strings: Failed to parse value type at line 1",
|
|
"");
|
|
checkSetError(
|
|
setting,
|
|
"Error parsing an array of strings: Unidentified trailing character ','--"
|
|
"-did you forget a '#'? at line 1",
|
|
"\"a\", \"b\", \"c\"");
|
|
}
|
|
|
|
TEST_F(ConfigSettingTest, setArrayDuration) {
|
|
ConfigSetting<std::vector<std::chrono::nanoseconds>> setting{
|
|
"test:value", std::vector<std::chrono::nanoseconds>{}, nullptr};
|
|
checkSet(setting, std::vector<std::chrono::nanoseconds>{90s}, "[\"1m30s\"]");
|
|
}
|
|
|
|
TEST_F(ConfigSettingTest, setArrayOptional) {
|
|
ConfigSetting<std::vector<std::optional<std::string>>> setting{
|
|
"test:value", std::vector<std::optional<std::string>>{}, nullptr};
|
|
checkSet(
|
|
setting, std::vector<std::optional<std::string>>{"foo"}, "[\"foo\"]");
|
|
}
|