// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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 // // http://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. // Author: Zhangyi Chen (chenzhangyi01@baidu.com) // Date: 2017/11/06 10:57:08 #include "butil/popen.h" #include "butil/errno.h" #include "butil/strings/string_piece.h" #include "butil/build_config.h" #include <gtest/gtest.h> namespace butil { extern int read_command_output_through_clone(std::ostream&, const char*); extern int read_command_output_through_popen(std::ostream&, const char*); } namespace { class PopenTest : public testing::Test { }; TEST(PopenTest, posix_popen) { std::ostringstream oss; int rc = butil::read_command_output_through_popen(oss, "echo \"Hello World\""); ASSERT_EQ(0, rc) << berror(errno); ASSERT_EQ("Hello World\n", oss.str()); oss.str(""); rc = butil::read_command_output_through_popen(oss, "exit 1"); EXPECT_EQ(1, rc) << berror(errno); ASSERT_TRUE(oss.str().empty()) << oss.str(); oss.str(""); rc = butil::read_command_output_through_popen(oss, "kill -9 $$"); ASSERT_EQ(-1, rc); ASSERT_EQ(errno, ECHILD); ASSERT_TRUE(butil::StringPiece(oss.str()).ends_with("was killed by signal 9")); oss.str(""); rc = butil::read_command_output_through_popen(oss, "kill -15 $$"); ASSERT_EQ(-1, rc); ASSERT_EQ(errno, ECHILD); ASSERT_TRUE(butil::StringPiece(oss.str()).ends_with("was killed by signal 15")); // TODO(zhujiashun): Fix this in macos /* oss.str(""); ASSERT_EQ(0, butil::read_command_output_through_popen(oss, "for i in `seq 1 100000`; do echo -n '=' ; done")); ASSERT_EQ(100000u, oss.str().length()); std::string expected; expected.resize(100000, '='); ASSERT_EQ(expected, oss.str()); */ } #if defined(OS_LINUX) TEST(PopenTest, clone) { std::ostringstream oss; int rc = butil::read_command_output_through_clone(oss, "echo \"Hello World\""); ASSERT_EQ(0, rc) << berror(errno); ASSERT_EQ("Hello World\n", oss.str()); oss.str(""); rc = butil::read_command_output_through_clone(oss, "exit 1"); ASSERT_EQ(1, rc) << berror(errno); ASSERT_TRUE(oss.str().empty()) << oss.str(); oss.str(""); rc = butil::read_command_output_through_clone(oss, "kill -9 $$"); ASSERT_EQ(-1, rc); ASSERT_EQ(errno, ECHILD); ASSERT_TRUE(butil::StringPiece(oss.str()).ends_with("was killed by signal 9")); oss.str(""); rc = butil::read_command_output_through_clone(oss, "kill -15 $$"); ASSERT_EQ(-1, rc); ASSERT_EQ(errno, ECHILD); ASSERT_TRUE(butil::StringPiece(oss.str()).ends_with("was killed by signal 15")); oss.str(""); ASSERT_EQ(0, butil::read_command_output_through_clone(oss, "for i in `seq 1 100000`; do echo -n '=' ; done")); ASSERT_EQ(100000u, oss.str().length()); std::string expected; expected.resize(100000, '='); ASSERT_EQ(expected, oss.str()); } struct CounterArg { volatile int64_t counter; volatile bool stop; }; static void* counter_thread(void* args) { CounterArg* ca = (CounterArg*)args; while (!ca->stop) { ++ca->counter; } return NULL; } static int fork_thread(void* arg) { usleep(100 * 1000); _exit(0); } const int CHILD_STACK_SIZE = 64 * 1024; TEST(PopenTest, does_vfork_suspend_all_threads) { pthread_t tid; CounterArg ca = { 0 , false }; ASSERT_EQ(0, pthread_create(&tid, NULL, counter_thread, &ca)); usleep(100 * 1000); char* child_stack_mem = (char*)malloc(CHILD_STACK_SIZE); void* child_stack = child_stack_mem + CHILD_STACK_SIZE; const int64_t counter_before_fork = ca.counter; pid_t cpid = clone(fork_thread, child_stack, CLONE_VFORK, NULL); const int64_t counter_after_fork = ca.counter; usleep(100 * 1000); const int64_t counter_after_sleep = ca.counter; int ws; ca.stop = true; pthread_join(tid, NULL); std::cout << "bc=" << counter_before_fork << " ac=" << counter_after_fork << " as=" << counter_after_sleep << std::endl; ASSERT_EQ(cpid, waitpid(cpid, &ws, __WALL)); } #endif // OS_LINUX }