Home | History | Annotate | Download | only in adb
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "shell_service.h"
     18 
     19 #include <gtest/gtest.h>
     20 
     21 #include <signal.h>
     22 
     23 #include <string>
     24 #include <vector>
     25 
     26 #include <android-base/strings.h>
     27 
     28 #include "adb.h"
     29 #include "adb_io.h"
     30 #include "sysdeps.h"
     31 
     32 class ShellServiceTest : public ::testing::Test {
     33   public:
     34     static void SetUpTestCase() {
     35         // This is normally done in main.cpp.
     36         saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
     37 
     38     }
     39 
     40     static void TearDownTestCase() {
     41         signal(SIGPIPE, saved_sigpipe_handler_);
     42     }
     43 
     44     // Helpers to start and cleanup a subprocess. Cleanup normally does not
     45     // need to be called manually unless multiple subprocesses are run from
     46     // a single test.
     47     void StartTestSubprocess(const char* command, SubprocessType type,
     48                              SubprocessProtocol protocol);
     49     void CleanupTestSubprocess();
     50 
     51     virtual void TearDown() override {
     52         void CleanupTestSubprocess();
     53     }
     54 
     55     static sighandler_t saved_sigpipe_handler_;
     56 
     57     int subprocess_fd_ = -1;
     58 };
     59 
     60 sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
     61 
     62 void ShellServiceTest::StartTestSubprocess(
     63         const char* command, SubprocessType type, SubprocessProtocol protocol) {
     64     subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
     65     ASSERT_TRUE(subprocess_fd_ >= 0);
     66 }
     67 
     68 void ShellServiceTest::CleanupTestSubprocess() {
     69     if (subprocess_fd_ >= 0) {
     70         adb_close(subprocess_fd_);
     71         subprocess_fd_ = -1;
     72     }
     73 }
     74 
     75 namespace {
     76 
     77 // Reads raw data from |fd| until it closes or errors.
     78 std::string ReadRaw(int fd) {
     79     char buffer[1024];
     80     char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
     81 
     82     while (1) {
     83         int bytes = adb_read(fd, cur_ptr, end_ptr - cur_ptr);
     84         if (bytes <= 0) {
     85             return std::string(buffer, cur_ptr);
     86         }
     87         cur_ptr += bytes;
     88     }
     89 }
     90 
     91 // Reads shell protocol data from |fd| until it closes or errors. Fills
     92 // |stdout| and |stderr| with their respective data, and returns the exit code
     93 // read from the protocol or -1 if an exit code packet was not received.
     94 int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
     95     int exit_code = -1;
     96     stdout->clear();
     97     stderr->clear();
     98 
     99     ShellProtocol* protocol = new ShellProtocol(fd);
    100     while (protocol->Read()) {
    101         switch (protocol->id()) {
    102             case ShellProtocol::kIdStdout:
    103                 stdout->append(protocol->data(), protocol->data_length());
    104                 break;
    105             case ShellProtocol::kIdStderr:
    106                 stderr->append(protocol->data(), protocol->data_length());
    107                 break;
    108             case ShellProtocol::kIdExit:
    109                 EXPECT_EQ(-1, exit_code) << "Multiple exit packets received";
    110                 EXPECT_EQ(1u, protocol->data_length());
    111                 exit_code = protocol->data()[0];
    112                 break;
    113             default:
    114                 ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
    115         }
    116     }
    117     delete protocol;
    118 
    119     return exit_code;
    120 }
    121 
    122 // Checks if each line in |lines| exists in the same order in |output|. Blank
    123 // lines in |output| are ignored for simplicity.
    124 bool ExpectLinesEqual(const std::string& output,
    125                       const std::vector<std::string>& lines) {
    126     auto output_lines = android::base::Split(output, "\r\n");
    127     size_t i = 0;
    128 
    129     for (const std::string& line : lines) {
    130         // Skip empty lines in output.
    131         while (i < output_lines.size() && output_lines[i].empty()) {
    132             ++i;
    133         }
    134         if (i >= output_lines.size()) {
    135             ADD_FAILURE() << "Ran out of output lines";
    136             return false;
    137         }
    138         EXPECT_EQ(line, output_lines[i]);
    139         ++i;
    140     }
    141 
    142     while (i < output_lines.size() && output_lines[i].empty()) {
    143         ++i;
    144     }
    145     EXPECT_EQ(i, output_lines.size()) << "Found unmatched output lines";
    146     return true;
    147 }
    148 
    149 }  // namespace
    150 
    151 // Tests a raw subprocess with no protocol.
    152 TEST_F(ShellServiceTest, RawNoProtocolSubprocess) {
    153     // [ -t 0 ] checks if stdin is connected to a terminal.
    154     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    155             "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
    156             SubprocessType::kRaw, SubprocessProtocol::kNone));
    157 
    158     // [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without
    159     // the shell protocol we should always force a PTY to ensure proper cleanup.
    160     ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
    161 }
    162 
    163 // Tests a PTY subprocess with no protocol.
    164 TEST_F(ShellServiceTest, PtyNoProtocolSubprocess) {
    165     // [ -t 0 ] checks if stdin is connected to a terminal.
    166     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    167             "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
    168             SubprocessType::kPty, SubprocessProtocol::kNone));
    169 
    170     // [ -t 0 ] == 0 means we have a terminal (PTY).
    171     ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
    172 }
    173 
    174 // Tests a raw subprocess with the shell protocol.
    175 TEST_F(ShellServiceTest, RawShellProtocolSubprocess) {
    176     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    177             "echo foo; echo bar >&2; echo baz; exit 24",
    178             SubprocessType::kRaw, SubprocessProtocol::kShell));
    179 
    180     std::string stdout, stderr;
    181     EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
    182     ExpectLinesEqual(stdout, {"foo", "baz"});
    183     ExpectLinesEqual(stderr, {"bar"});
    184 }
    185 
    186 // Tests a PTY subprocess with the shell protocol.
    187 TEST_F(ShellServiceTest, PtyShellProtocolSubprocess) {
    188     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    189             "echo foo; echo bar >&2; echo baz; exit 50",
    190             SubprocessType::kPty, SubprocessProtocol::kShell));
    191 
    192     // PTY always combines stdout and stderr but the shell protocol should
    193     // still give us an exit code.
    194     std::string stdout, stderr;
    195     EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
    196     ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
    197     ExpectLinesEqual(stderr, {});
    198 }
    199 
    200 // Tests an interactive PTY session.
    201 TEST_F(ShellServiceTest, InteractivePtySubprocess) {
    202     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    203             "", SubprocessType::kPty, SubprocessProtocol::kShell));
    204 
    205     // Use variable substitution so echoed input is different from output.
    206     const char* commands[] = {"TEST_STR=abc123",
    207                               "echo --${TEST_STR}--",
    208                               "exit"};
    209 
    210     ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
    211     for (std::string command : commands) {
    212         // Interactive shell requires a newline to complete each command.
    213         command.push_back('\n');
    214         memcpy(protocol->data(), command.data(), command.length());
    215         ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, command.length()));
    216     }
    217     delete protocol;
    218 
    219     std::string stdout, stderr;
    220     EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
    221     // An unpredictable command prompt makes parsing exact output difficult but
    222     // it should at least contain echoed input and the expected output.
    223     for (const char* command : commands) {
    224         EXPECT_FALSE(stdout.find(command) == std::string::npos);
    225     }
    226     EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
    227 }
    228 
    229 // Tests closing raw subprocess stdin.
    230 TEST_F(ShellServiceTest, CloseClientStdin) {
    231     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    232             "cat; echo TEST_DONE",
    233             SubprocessType::kRaw, SubprocessProtocol::kShell));
    234 
    235     std::string input = "foo\nbar";
    236     ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
    237     memcpy(protocol->data(), input.data(), input.length());
    238     ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
    239     ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
    240     delete protocol;
    241 
    242     std::string stdout, stderr;
    243     EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
    244     ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
    245     ExpectLinesEqual(stderr, {});
    246 }
    247 
    248 // Tests that nothing breaks when the stdin/stdout pipe closes.
    249 TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
    250     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    251             "exec 0<&-; exec 1>&-; echo bar >&2",
    252             SubprocessType::kRaw, SubprocessProtocol::kShell));
    253 
    254     std::string stdout, stderr;
    255     EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
    256     ExpectLinesEqual(stdout, {});
    257     ExpectLinesEqual(stderr, {"bar"});
    258 }
    259 
    260 // Tests that nothing breaks when the stderr pipe closes.
    261 TEST_F(ShellServiceTest, CloseStderrSubprocess) {
    262     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
    263             "exec 2>&-; echo foo",
    264             SubprocessType::kRaw, SubprocessProtocol::kShell));
    265 
    266     std::string stdout, stderr;
    267     EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
    268     ExpectLinesEqual(stdout, {"foo"});
    269     ExpectLinesEqual(stderr, {});
    270 }
    271