blob: 146e87bb1bc2f26e233e73fd552c256d4a55b2c8 [file] [log] [blame]
// Copyright 2022 The Centipede 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
//
// https://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.
#include "./centipede/command.h"
#include <signal.h>
#include <sys/wait.h> // NOLINT(for WTERMSIG)
#include <cstdlib>
#include <string>
#include "gtest/gtest.h"
#include "absl/strings/substitute.h"
#include "./centipede/logging.h"
#include "./centipede/test_util.h"
#include "./centipede/util.h"
namespace centipede {
namespace {
TEST(CommandTest, ToString) {
EXPECT_EQ(Command("x").ToString(), "x");
EXPECT_EQ(Command("path", {"arg1", "arg2"}).ToString(),
"path \\\narg1 \\\narg2");
EXPECT_EQ(Command("x", {}, {"K1=V1", "K2=V2"}).ToString(),
"K1=V1 \\\nK2=V2 \\\nx");
EXPECT_EQ(Command("x", {}, {}, "out").ToString(), "x \\\n> out");
EXPECT_EQ(Command("x", {}, {}, "", "err").ToString(), "x \\\n2> err");
EXPECT_EQ(Command("x", {}, {}, "out", "err").ToString(),
"x \\\n> out \\\n2> err");
EXPECT_EQ(Command("x", {}, {}, "out", "out").ToString(),
"x \\\n> out \\\n2>&1");
}
TEST(CommandTest, Execute) {
// Check for default exit code.
Command echo("echo");
EXPECT_EQ(echo.Execute(), 0);
EXPECT_FALSE(EarlyExitRequested());
// Check for exit code 7.
Command exit7("bash -c 'exit 7'");
EXPECT_EQ(exit7.Execute(), 7);
EXPECT_FALSE(EarlyExitRequested());
}
TEST(CommandDeathTest, Execute) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
// Test for interrupt handling.
const auto self_sigint_lambda = []() {
Command self_sigint("bash -c 'kill -SIGINT $$'");
self_sigint.Execute();
if (EarlyExitRequested()) {
LOG(INFO) << "Early exit requested";
exit(ExitCode());
}
};
EXPECT_DEATH(self_sigint_lambda(), "Early exit requested");
}
TEST(CommandTest, InputFileWildCard) {
std::string_view command_line = "foo bar @@ baz";
Command cmd(command_line, {}, {}, "", "", absl::Seconds(2), "TEMP_FILE");
EXPECT_EQ(cmd.ToString(), "foo bar TEMP_FILE baz");
}
TEST(CommandTest, ForkServer) {
const std::string test_tmpdir = GetTestTempDir(test_info_->name());
const std::string helper =
GetDataDependencyFilepath("centipede/command_test_helper");
// TODO(ussuri): Dedupe these testcases.
{
const std::string input = "success";
const std::string log = std::filesystem::path{test_tmpdir} / input;
Command cmd(helper, {input}, {}, log, log);
EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer"));
EXPECT_EQ(cmd.Execute(), EXIT_SUCCESS);
std::string log_contents;
ReadFromLocalFile(log, log_contents);
EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input));
}
{
const std::string input = "fail";
const std::string log = std::filesystem::path{test_tmpdir} / input;
Command cmd(helper, {input}, {}, log, log);
EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer"));
EXPECT_EQ(cmd.Execute(), EXIT_FAILURE);
std::string log_contents;
ReadFromLocalFile(log, log_contents);
EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input));
}
{
const std::string input = "ret42";
const std::string log = std::filesystem::path{test_tmpdir} / input;
Command cmd(helper, {input}, {}, log, log);
EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer"));
EXPECT_EQ(cmd.Execute(), 42);
std::string log_contents;
ReadFromLocalFile(log, log_contents);
EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input));
}
{
const std::string input = "abort";
const std::string log = std::filesystem::path{test_tmpdir} / input;
Command cmd(helper, {input}, {}, log, log);
EXPECT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer"));
EXPECT_EQ(WTERMSIG(cmd.Execute()), SIGABRT);
std::string log_contents;
ReadFromLocalFile(log, log_contents);
EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input));
}
{
const std::string input = "hang";
const std::string log = std::filesystem::path{test_tmpdir} / input;
constexpr auto kTimeout = absl::Seconds(2);
Command cmd(helper, {input}, {}, log, log, kTimeout);
ASSERT_TRUE(cmd.StartForkServer(test_tmpdir, "ForkServer"));
EXPECT_EQ(cmd.Execute(), EXIT_FAILURE);
std::string log_contents;
ReadFromLocalFile(log, log_contents);
EXPECT_EQ(log_contents, absl::Substitute("Got input: $0", input));
}
// TODO(kcc): [impl] test what happens if the child is interrupted.
}
} // namespace
} // namespace centipede