10955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell/*
20955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * Copyright (C) 2015 The Android Open Source Project
30955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell *
40955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * Licensed under the Apache License, Version 2.0 (the "License");
50955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * you may not use this file except in compliance with the License.
60955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * You may obtain a copy of the License at
70955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell *
80955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell *      http://www.apache.org/licenses/LICENSE-2.0
90955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell *
100955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * Unless required by applicable law or agreed to in writing, software
110955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * distributed under the License is distributed on an "AS IS" BASIS,
120955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * See the License for the specific language governing permissions and
140955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell * limitations under the License.
150955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell */
160955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
170955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include "shell_service.h"
180955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
190955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include <gtest/gtest.h>
200955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
210955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include <signal.h>
220955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
230955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include <string>
240955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include <vector>
250955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
264f71319df011d796a60a43fc1bc68e16fbf7d321Elliott Hughes#include <android-base/strings.h>
270955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
280955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include "adb.h"
290955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include "adb_io.h"
300955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell#include "sysdeps.h"
310955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
320955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellclass ShellServiceTest : public ::testing::Test {
330955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell  public:
340955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    static void SetUpTestCase() {
350955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        // This is normally done in main.cpp.
360955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
370955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
380955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
390955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
400955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    static void TearDownTestCase() {
410955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        signal(SIGPIPE, saved_sigpipe_handler_);
420955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
430955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
440955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // Helpers to start and cleanup a subprocess. Cleanup normally does not
450955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // need to be called manually unless multiple subprocesses are run from
460955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // a single test.
470955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    void StartTestSubprocess(const char* command, SubprocessType type,
480955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                             SubprocessProtocol protocol);
490955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    void CleanupTestSubprocess();
500955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
510955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    virtual void TearDown() override {
520955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        void CleanupTestSubprocess();
530955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
540955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
550955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    static sighandler_t saved_sigpipe_handler_;
560955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
570955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    int subprocess_fd_ = -1;
580955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell};
590955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
600955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellsighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
610955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
620955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellvoid ShellServiceTest::StartTestSubprocess(
630955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        const char* command, SubprocessType type, SubprocessProtocol protocol) {
6418ddf5c6a233bd56d20548fd834c0ecbf8216410Elliott Hughes    subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
650955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_TRUE(subprocess_fd_ >= 0);
660955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
670955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
680955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellvoid ShellServiceTest::CleanupTestSubprocess() {
690955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    if (subprocess_fd_ >= 0) {
700955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        adb_close(subprocess_fd_);
710955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        subprocess_fd_ = -1;
720955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
730955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
740955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
750955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellnamespace {
760955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
770955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Reads raw data from |fd| until it closes or errors.
780955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellstd::string ReadRaw(int fd) {
790955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    char buffer[1024];
800955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
810955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
820955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    while (1) {
830955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        int bytes = adb_read(fd, cur_ptr, end_ptr - cur_ptr);
840955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        if (bytes <= 0) {
850955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            return std::string(buffer, cur_ptr);
860955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        }
870955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        cur_ptr += bytes;
880955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
890955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
900955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
910955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Reads shell protocol data from |fd| until it closes or errors. Fills
920955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// |stdout| and |stderr| with their respective data, and returns the exit code
930955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// read from the protocol or -1 if an exit code packet was not received.
940955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellint ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
950955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    int exit_code = -1;
960955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    stdout->clear();
970955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    stderr->clear();
980955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
990955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ShellProtocol* protocol = new ShellProtocol(fd);
1000955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    while (protocol->Read()) {
1010955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        switch (protocol->id()) {
1020955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            case ShellProtocol::kIdStdout:
1030955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                stdout->append(protocol->data(), protocol->data_length());
1040955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                break;
1050955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            case ShellProtocol::kIdStderr:
1060955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                stderr->append(protocol->data(), protocol->data_length());
1070955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                break;
1080955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            case ShellProtocol::kIdExit:
1090955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                EXPECT_EQ(-1, exit_code) << "Multiple exit packets received";
1100955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                EXPECT_EQ(1u, protocol->data_length());
1110955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                exit_code = protocol->data()[0];
1120955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                break;
1130955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            default:
1140955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
1150955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        }
1160955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
1170955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    delete protocol;
1180955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1190955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    return exit_code;
1200955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
1210955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1220955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Checks if each line in |lines| exists in the same order in |output|. Blank
1230955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// lines in |output| are ignored for simplicity.
1240955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursellbool ExpectLinesEqual(const std::string& output,
1250955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                      const std::vector<std::string>& lines) {
1260955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    auto output_lines = android::base::Split(output, "\r\n");
1270955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    size_t i = 0;
1280955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1290955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    for (const std::string& line : lines) {
1300955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        // Skip empty lines in output.
1310955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        while (i < output_lines.size() && output_lines[i].empty()) {
1320955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            ++i;
1330955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        }
1340955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        if (i >= output_lines.size()) {
1350955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            ADD_FAILURE() << "Ran out of output lines";
1360955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            return false;
1370955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        }
1380955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        EXPECT_EQ(line, output_lines[i]);
1390955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        ++i;
1400955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
1410955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1420955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    while (i < output_lines.size() && output_lines[i].empty()) {
1430955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        ++i;
1440955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
1450955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    EXPECT_EQ(i, output_lines.size()) << "Found unmatched output lines";
1460955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    return true;
1470955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
1480955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1490955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}  // namespace
1500955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1510955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Tests a raw subprocess with no protocol.
1520955c66b226db7a7f34613f834f7b0a145fd407dDavid PursellTEST_F(ShellServiceTest, RawNoProtocolSubprocess) {
1530955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // [ -t 0 ] checks if stdin is connected to a terminal.
1540955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
1550955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
1560955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            SubprocessType::kRaw, SubprocessProtocol::kNone));
1570955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
15857dd5ae1e3004daec664263e24dc4dcf4475bb02David Pursell    // [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without
15957dd5ae1e3004daec664263e24dc4dcf4475bb02David Pursell    // the shell protocol we should always force a PTY to ensure proper cleanup.
16057dd5ae1e3004daec664263e24dc4dcf4475bb02David Pursell    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
1610955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
1620955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1630955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Tests a PTY subprocess with no protocol.
1640955c66b226db7a7f34613f834f7b0a145fd407dDavid PursellTEST_F(ShellServiceTest, PtyNoProtocolSubprocess) {
1650955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // [ -t 0 ] checks if stdin is connected to a terminal.
1660955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
1670955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
1680955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            SubprocessType::kPty, SubprocessProtocol::kNone));
1690955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1700955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // [ -t 0 ] == 0 means we have a terminal (PTY).
1710955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
1720955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
1730955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1740955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Tests a raw subprocess with the shell protocol.
1750955c66b226db7a7f34613f834f7b0a145fd407dDavid PursellTEST_F(ShellServiceTest, RawShellProtocolSubprocess) {
1760955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
1770955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            "echo foo; echo bar >&2; echo baz; exit 24",
1780955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            SubprocessType::kRaw, SubprocessProtocol::kShell));
1790955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1800955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    std::string stdout, stderr;
1810955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
1820955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stdout, {"foo", "baz"});
1830955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stderr, {"bar"});
1840955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
1850955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1860955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Tests a PTY subprocess with the shell protocol.
1870955c66b226db7a7f34613f834f7b0a145fd407dDavid PursellTEST_F(ShellServiceTest, PtyShellProtocolSubprocess) {
1880955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
1890955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            "echo foo; echo bar >&2; echo baz; exit 50",
1900955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            SubprocessType::kPty, SubprocessProtocol::kShell));
1910955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
1920955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // PTY always combines stdout and stderr but the shell protocol should
1930955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // still give us an exit code.
1940955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    std::string stdout, stderr;
1950955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
1960955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
1970955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stderr, {});
1980955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
1990955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2000955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Tests an interactive PTY session.
2010955c66b226db7a7f34613f834f7b0a145fd407dDavid PursellTEST_F(ShellServiceTest, InteractivePtySubprocess) {
2020955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
2030955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            "", SubprocessType::kPty, SubprocessProtocol::kShell));
2040955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2050955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // Use variable substitution so echoed input is different from output.
2060955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    const char* commands[] = {"TEST_STR=abc123",
2070955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                              "echo --${TEST_STR}--",
2080955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell                              "exit"};
2090955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2100955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
2110955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    for (std::string command : commands) {
2120955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        // Interactive shell requires a newline to complete each command.
2130955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        command.push_back('\n');
2140955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        memcpy(protocol->data(), command.data(), command.length());
2150955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, command.length()));
2160955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
2170955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    delete protocol;
2180955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2190955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    std::string stdout, stderr;
2200955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
2210955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // An unpredictable command prompt makes parsing exact output difficult but
2220955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    // it should at least contain echoed input and the expected output.
2230955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    for (const char* command : commands) {
2240955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell        EXPECT_FALSE(stdout.find(command) == std::string::npos);
2250955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    }
2260955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
2270955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
2280955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2291ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell// Tests closing raw subprocess stdin.
2301ed57f0dc333c0bc0800e222c569cca8a71deb89David PursellTEST_F(ShellServiceTest, CloseClientStdin) {
2311ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
2321ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell            "cat; echo TEST_DONE",
2331ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell            SubprocessType::kRaw, SubprocessProtocol::kShell));
2341ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell
2351ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    std::string input = "foo\nbar";
2361ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
2371ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    memcpy(protocol->data(), input.data(), input.length());
2381ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
2391ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
2401ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    delete protocol;
2411ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell
2421ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    std::string stdout, stderr;
2431ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
2441ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
2451ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell    ExpectLinesEqual(stderr, {});
2461ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell}
2471ed57f0dc333c0bc0800e222c569cca8a71deb89David Pursell
2480955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Tests that nothing breaks when the stdin/stdout pipe closes.
2490955c66b226db7a7f34613f834f7b0a145fd407dDavid PursellTEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
2500955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
2510955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            "exec 0<&-; exec 1>&-; echo bar >&2",
2520955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            SubprocessType::kRaw, SubprocessProtocol::kShell));
2530955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2540955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    std::string stdout, stderr;
2550955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
2560955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stdout, {});
2570955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stderr, {"bar"});
2580955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
2590955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2600955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell// Tests that nothing breaks when the stderr pipe closes.
2610955c66b226db7a7f34613f834f7b0a145fd407dDavid PursellTEST_F(ShellServiceTest, CloseStderrSubprocess) {
2620955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
2630955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            "exec 2>&-; echo foo",
2640955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell            SubprocessType::kRaw, SubprocessProtocol::kShell));
2650955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell
2660955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    std::string stdout, stderr;
2670955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
2680955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stdout, {"foo"});
2690955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell    ExpectLinesEqual(stderr, {});
2700955c66b226db7a7f34613f834f7b0a145fd407dDavid Pursell}
271