2017-06-24 04:12:28 +03:00
|
|
|
/*
|
2019-06-20 02:58:25 +03:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2017-06-24 04:12:28 +03:00
|
|
|
*
|
2019-06-20 02:58:25 +03:00
|
|
|
* This software may be used and distributed according to the terms of the
|
|
|
|
* GNU General Public License version 2.
|
2017-06-24 04:12:28 +03:00
|
|
|
*/
|
2019-10-11 15:26:59 +03:00
|
|
|
|
2017-06-24 04:12:28 +03:00
|
|
|
#include "eden/fs/testharness/HgRepo.h"
|
|
|
|
|
|
|
|
#include <folly/Exception.h>
|
2017-07-05 21:17:06 +03:00
|
|
|
#include <folly/File.h>
|
2017-06-24 04:12:28 +03:00
|
|
|
#include <folly/FileUtil.h>
|
|
|
|
#include <folly/String.h>
|
|
|
|
#include <folly/Subprocess.h>
|
2018-05-01 07:20:51 +03:00
|
|
|
#include <folly/logging/xlog.h>
|
2017-06-24 04:12:28 +03:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "eden/fs/model/Hash.h"
|
|
|
|
|
|
|
|
using folly::StringPiece;
|
|
|
|
using folly::Subprocess;
|
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
2018-08-24 01:37:30 +03:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
AbsolutePath findHgBinary() {
|
|
|
|
auto hgPath = getenv("EDEN_HG_BINARY");
|
|
|
|
if (hgPath) {
|
|
|
|
return realpath(hgPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search through $PATH if $EDEN_HG_BINARY was not explicitly specified
|
2017-06-24 04:12:28 +03:00
|
|
|
auto pathPtr = getenv("PATH");
|
|
|
|
if (!pathPtr) {
|
|
|
|
throw std::runtime_error("unable to find hg command: no PATH");
|
|
|
|
}
|
|
|
|
StringPiece pathEnv{pathPtr};
|
|
|
|
vector<string> pathEnvParts;
|
|
|
|
folly::split(":", pathEnv, pathEnvParts);
|
|
|
|
|
|
|
|
for (const auto& dir : pathEnvParts) {
|
|
|
|
for (const auto& name : {"hg.real", "hg"}) {
|
|
|
|
auto exePath = folly::to<string>(dir, "/", name);
|
|
|
|
XLOG(DBG5) << "Checking for hg at " << exePath;
|
|
|
|
if (access(exePath.c_str(), X_OK) == 0) {
|
2018-08-24 01:37:30 +03:00
|
|
|
return realpath(exePath);
|
2017-06-24 04:12:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-24 01:37:30 +03:00
|
|
|
|
|
|
|
throw std::runtime_error("unable to find hg in PATH");
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
HgRepo::HgRepo(AbsolutePathPiece path) : path_{path} {
|
|
|
|
hgCmd_ = findHgBinary();
|
2017-06-24 04:12:28 +03:00
|
|
|
XLOG(DBG1) << "Using hg command: " << hgCmd_;
|
|
|
|
|
|
|
|
// Set up hgEnv_
|
Fix code coverage causing test failures
Summary:
Sandcastle's [1] code coverage builds compile EdenFS and Hg with Clang's -fprofile-generate flag (or a related flag). By default, running a program with with that flag creates a `default.profraw` file in the current directory. This causes some tests to fail, such as HgBackingStoreTest.getTreeForCommit_reimports_tree_if_it_was_deleted_after_import, because `default.profraw` is unintentionally committed to the test repo:
```
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from HgBackingStoreTest
[ RUN ] HgBackingStoreTest.getTreeForCommit_reimports_tree_if_it_was_deleted_after_import
eden/fs/store/hg/test/HgBackingStoreTest.cpp:64: Failure
Value of: tree1->getEntryNames()
Expected: has 2 elements where
element #0 is equal to foo,
element #1 is equal to src
Actual: { default.profraw, foo, src }, which has 3 elements
[ FAILED ] HgBackingStoreTest.getTreeForCommit_reimports_tree_if_it_was_deleted_after_import (1563 ms)
[----------] 1 test from HgBackingStoreTest (1563 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1563 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] HgBackingStoreTest.getTreeForCommit_reimports_tree_if_it_was_deleted_after_import
```
When Sandcastle runs the tests, it sets the `LLVM_PROFILE_FILE` environment variable, which configures the path to `default.profraw`. EdenFS' HgRepo class is stripping this variable when invoking hg, so hg uses the default path and not the configured path.
Make HgRepo pass `LLVM_PROFILE_FILE` through to hg. This fixes some of EdenFS' tests when run by Sandcastle for code coverage purposes. (This does *not* fix running the tests manually (without setting `LLVM_PROFILE_FILE`), though.)
[1] Sandcastle is Facebook's continuous integration system.
Reviewed By: simpkins
Differential Revision: D15104832
fbshipit-source-id: 3929b9b0ab0904f2552ace7d8e4e4f4a4221192c
2019-04-27 00:08:42 +03:00
|
|
|
std::vector<const char*> passthroughVars{
|
|
|
|
{"HG_REAL_BIN", "LLVM_PROFILE_FILE", "PATH"}};
|
2018-08-24 22:14:03 +03:00
|
|
|
for (const char* varName : passthroughVars) {
|
|
|
|
auto value = getenv(varName);
|
|
|
|
if (value) {
|
|
|
|
hgEnv_.push_back(folly::to<string>(varName, "=", value));
|
|
|
|
}
|
2018-08-24 01:37:30 +03:00
|
|
|
}
|
2017-06-24 04:12:28 +03:00
|
|
|
hgEnv_.push_back("HGPLAIN=1");
|
2018-06-14 05:34:59 +03:00
|
|
|
hgEnv_.push_back("HGRCPATH=");
|
2017-06-24 04:12:28 +03:00
|
|
|
hgEnv_.push_back("CHGDISABLE=1");
|
2018-09-18 22:00:27 +03:00
|
|
|
hgEnv_.push_back("NOSCMLOG=1");
|
2017-06-24 04:12:28 +03:00
|
|
|
hgEnv_.push_back("LOCALE=C");
|
|
|
|
}
|
|
|
|
|
|
|
|
string HgRepo::hg(vector<string> args) {
|
2018-06-14 05:35:02 +03:00
|
|
|
auto process = invokeHg(std::move(args));
|
|
|
|
const auto outputs{process.communicate()};
|
|
|
|
process.waitChecked();
|
|
|
|
return outputs.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
Subprocess HgRepo::invokeHg(vector<string> args) {
|
|
|
|
return invokeHg(
|
|
|
|
std::move(args), Subprocess::Options().chdir(path_.value()).pipeStdout());
|
|
|
|
}
|
|
|
|
|
|
|
|
Subprocess HgRepo::invokeHg(
|
|
|
|
vector<string> args,
|
|
|
|
const Subprocess::Options& options) {
|
2017-06-24 04:12:28 +03:00
|
|
|
args.insert(args.begin(), "hg");
|
|
|
|
|
|
|
|
XLOG(DBG1) << "repo " << path_ << " running: " << folly::join(" ", args);
|
2018-06-14 05:35:02 +03:00
|
|
|
return Subprocess(args, options, hgCmd_.value().c_str(), &hgEnv_);
|
2017-06-24 04:12:28 +03:00
|
|
|
}
|
|
|
|
|
2018-06-14 05:35:02 +03:00
|
|
|
void HgRepo::hgInit(std::vector<std::string> extraArgs) {
|
2017-06-24 04:12:28 +03:00
|
|
|
XLOG(DBG1) << "creating new hg repository at " << path_;
|
|
|
|
|
|
|
|
// Invoke Subprocess directly here rather than using our hg() helper
|
|
|
|
// function. The hg() function requires the repository directory to already
|
|
|
|
// exist.
|
2018-06-14 05:35:02 +03:00
|
|
|
std::vector<std::string> args = {"hg", "init", path_.value()};
|
|
|
|
args.insert(args.end(), extraArgs.begin(), extraArgs.end());
|
|
|
|
Subprocess p(args, Subprocess::Options(), hgCmd_.value().c_str(), &hgEnv_);
|
|
|
|
p.waitChecked();
|
|
|
|
}
|
|
|
|
|
2019-05-23 01:55:11 +03:00
|
|
|
void HgRepo::enableTreeManifest(AbsolutePathPiece cacheDirectory) {
|
|
|
|
appendToHgrc(
|
|
|
|
"[extensions]\n"
|
|
|
|
"remotefilelog =\n"
|
|
|
|
"treemanifest =\n"
|
|
|
|
"[treemanifest]\n"
|
|
|
|
"treeonly = true\n"
|
|
|
|
"[remotefilelog]\n"
|
|
|
|
"reponame = test\n"
|
|
|
|
"cachepath = " +
|
|
|
|
cacheDirectory.value().str() + "\n");
|
|
|
|
}
|
|
|
|
|
2018-06-14 05:35:02 +03:00
|
|
|
void HgRepo::cloneFrom(
|
|
|
|
StringPiece serverRepoUrl,
|
|
|
|
std::vector<std::string> extraArgs) {
|
|
|
|
XLOG(DBG1) << "cloning new hg repository at " << path_ << " from "
|
|
|
|
<< serverRepoUrl;
|
|
|
|
|
|
|
|
std::vector<std::string> args = {"hg", "clone"};
|
|
|
|
args.insert(args.end(), extraArgs.begin(), extraArgs.end());
|
|
|
|
args.push_back(serverRepoUrl.str());
|
|
|
|
args.push_back(path_.value());
|
|
|
|
XLOG(DBG1) << "running: " << folly::join(" ", args);
|
|
|
|
|
|
|
|
Subprocess p(args, Subprocess::Options(), hgCmd_.value().c_str(), &hgEnv_);
|
2017-06-24 04:12:28 +03:00
|
|
|
p.waitChecked();
|
|
|
|
}
|
|
|
|
|
2017-07-05 21:17:06 +03:00
|
|
|
void HgRepo::appendToHgrc(folly::StringPiece data) {
|
2018-05-12 00:44:31 +03:00
|
|
|
auto hgrcPath = path_ + ".hg"_pc + "hgrc"_pc;
|
2017-07-05 21:17:06 +03:00
|
|
|
folly::File hgrc{hgrcPath.stringPiece(), O_WRONLY | O_APPEND | O_CREAT};
|
|
|
|
if (folly::writeFull(hgrc.fd(), data.data(), data.size()) < 0) {
|
|
|
|
folly::throwSystemError("error writing to ", hgrcPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void HgRepo::appendToHgrc(const std::vector<std::string>& lines) {
|
2018-06-14 05:35:02 +03:00
|
|
|
appendToHgrc(folly::join("\n", lines) + "\n");
|
2017-07-05 21:17:06 +03:00
|
|
|
}
|
|
|
|
|
2017-06-24 04:12:28 +03:00
|
|
|
Hash HgRepo::commit(StringPiece message) {
|
|
|
|
hg("commit",
|
|
|
|
"-u",
|
|
|
|
"Test User <user@example.com>",
|
|
|
|
"-d",
|
|
|
|
"2017-01-01 13:00:00",
|
|
|
|
"-m",
|
|
|
|
message.str());
|
|
|
|
auto output = hg("log", "-r.", "-T{node}\\n");
|
|
|
|
return Hash{folly::rtrimWhitespace(output)};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:23:34 +03:00
|
|
|
Hash HgRepo::getManifestForCommit(Hash commit) {
|
|
|
|
auto output = hg("log", "-r", commit.toString(), "-T{manifest}\\n");
|
|
|
|
return Hash{folly::rtrimWhitespace(output)};
|
|
|
|
}
|
|
|
|
|
2017-06-24 04:12:28 +03:00
|
|
|
void HgRepo::mkdir(RelativePathPiece path, mode_t permissions) {
|
|
|
|
auto fullPath = path_ + path;
|
|
|
|
auto rc = ::mkdir(fullPath.value().c_str(), permissions);
|
|
|
|
folly::checkUnixError(rc, "mkdir ", fullPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HgRepo::writeFile(
|
|
|
|
RelativePathPiece path,
|
|
|
|
StringPiece contents,
|
|
|
|
mode_t permissions) {
|
|
|
|
auto fullPath = path_ + path;
|
|
|
|
folly::writeFileAtomic(fullPath.value(), contents, permissions);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HgRepo::symlink(StringPiece contents, RelativePathPiece path) {
|
|
|
|
auto fullPath = path_ + path;
|
|
|
|
auto rc = ::symlink(contents.str().c_str(), fullPath.value().c_str());
|
|
|
|
checkUnixError(rc, "error creating symlink at ", path);
|
|
|
|
}
|
2017-11-04 01:58:04 +03:00
|
|
|
} // namespace eden
|
|
|
|
} // namespace facebook
|